VirtualBox

source: vbox/trunk/src/VBox/Devices/VMMDev/VMMDevHGCM.cpp@ 75555

Last change on this file since 75555 was 75555, checked in by vboxsync, 6 years ago

VBoxDevHGCM.cpp: doxygen fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 86.7 KB
Line 
1/* $Id: VMMDevHGCM.cpp 75555 2018-11-18 17:54:07Z vboxsync $ */
2/** @file
3 * VMMDev - HGCM - Host-Guest Communication Manager Device.
4 */
5
6/*
7 * Copyright (C) 2006-2018 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
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_VMM
23#include <iprt/alloc.h>
24#include <iprt/asm.h>
25#include <iprt/assert.h>
26#include <iprt/param.h>
27#include <iprt/string.h>
28
29#include <VBox/AssertGuest.h>
30#include <VBox/err.h>
31#include <VBox/hgcmsvc.h>
32#include <VBox/log.h>
33
34#include "VMMDevHGCM.h"
35
36#ifdef DEBUG
37# define VBOX_STRICT_GUEST
38#endif
39
40#ifdef VBOX_WITH_DTRACE
41# include "dtrace/VBoxDD.h"
42#else
43# define VBOXDD_HGCMCALL_ENTER(a,b,c,d) do { } while (0)
44# define VBOXDD_HGCMCALL_COMPLETED_REQ(a,b) do { } while (0)
45# define VBOXDD_HGCMCALL_COMPLETED_EMT(a,b) do { } while (0)
46# define VBOXDD_HGCMCALL_COMPLETED_DONE(a,b,c,d) do { } while (0)
47#endif
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53typedef enum VBOXHGCMCMDTYPE
54{
55 VBOXHGCMCMDTYPE_LOADSTATE = 0,
56 VBOXHGCMCMDTYPE_CONNECT,
57 VBOXHGCMCMDTYPE_DISCONNECT,
58 VBOXHGCMCMDTYPE_CALL,
59 VBOXHGCMCMDTYPE_SizeHack = 0x7fffffff
60} VBOXHGCMCMDTYPE;
61
62/**
63 * Information about a 32 or 64 bit parameter.
64 */
65typedef struct VBOXHGCMPARMVAL
66{
67 /** Actual value. Both 32 and 64 bit is saved here. */
68 uint64_t u64Value;
69
70 /** Offset from the start of the request where the value is stored. */
71 uint32_t offValue;
72
73 /** Size of the value: 4 for 32 bit and 8 for 64 bit. */
74 uint32_t cbValue;
75
76} VBOXHGCMPARMVAL;
77
78/**
79 * Information about a pointer parameter.
80 */
81typedef struct VBOXHGCMPARMPTR
82{
83 /** Size of the buffer described by the pointer parameter. */
84 uint32_t cbData;
85
86 /** Offset in the first physical page of the region. */
87 uint32_t offFirstPage;
88
89 /** How many pages. */
90 uint32_t cPages;
91
92 /** How the buffer should be copied VBOX_HGCM_F_PARM_*. */
93 uint32_t fu32Direction;
94
95 /** Pointer to array of the GC physical addresses for these pages.
96 * It is assumed that the physical address of the locked resident guest page
97 * does not change. */
98 RTGCPHYS *paPages;
99
100 /** For single page requests. */
101 RTGCPHYS GCPhysSinglePage;
102
103} VBOXHGCMPARMPTR;
104
105/**
106 * Information about a guest HGCM parameter.
107 */
108typedef struct VBOXHGCMGUESTPARM
109{
110 /** The parameter type. */
111 HGCMFunctionParameterType enmType;
112
113 union
114 {
115 VBOXHGCMPARMVAL val;
116 VBOXHGCMPARMPTR ptr;
117 } u;
118
119} VBOXHGCMGUESTPARM;
120
121typedef struct VBOXHGCMCMD
122{
123 /** Active commands, list is protected by critsectHGCMCmdList. */
124 RTLISTNODE node;
125
126 /** The type of the command. */
127 VBOXHGCMCMDTYPE enmCmdType;
128
129 /** Whether the command was cancelled by the guest. */
130 bool fCancelled;
131
132 /** Whether the command was restored from saved state. */
133 bool fRestored;
134
135 /** Set if allocated from the memory cache, clear if heap. */
136 bool fMemCache;
137
138 /** GC physical address of the guest request. */
139 RTGCPHYS GCPhys;
140
141 /** Request packet size. */
142 uint32_t cbRequest;
143
144 /** The type of the guest request. */
145 VMMDevRequestType enmRequestType;
146
147 /** Pointer to the locked request, NULL if not locked. */
148 void *pvReqLocked;
149 /** The PGM lock for GCPhys if pvReqLocked is not NULL. */
150 PGMPAGEMAPLOCK ReqMapLock;
151
152 /** The STAM_GET_TS() value when the request arrived. */
153 uint64_t tsArrival;
154 /** The STAM_GET_TS() value when the hgcmCompleted() is called. */
155 uint64_t tsComplete;
156
157 union
158 {
159 struct
160 {
161 uint32_t u32ClientID;
162 HGCMServiceLocation *pLoc; /**< Allocated after this structure. */
163 } connect;
164
165 struct
166 {
167 uint32_t u32ClientID;
168 } disconnect;
169
170 struct
171 {
172 /* Number of elements in paGuestParms and paHostParms arrays. */
173 uint32_t cParms;
174
175 uint32_t u32ClientID;
176
177 uint32_t u32Function;
178
179 /** Pointer to information about guest parameters in case of a Call request.
180 * Follows this structure in the same memory block.
181 */
182 VBOXHGCMGUESTPARM *paGuestParms;
183
184 /** Pointer to converted host parameters in case of a Call request.
185 * Follows this structure in the same memory block.
186 */
187 VBOXHGCMSVCPARM *paHostParms;
188
189 /* VBOXHGCMGUESTPARM[] */
190 /* VBOXHGCMSVCPARM[] */
191 } call;
192 } u;
193} VBOXHGCMCMD;
194
195
196/**
197 * Version for the memory cache.
198 */
199typedef struct VBOXHGCMCMDCACHED
200{
201 VBOXHGCMCMD Core; /**< 112 */
202 VBOXHGCMGUESTPARM aGuestParms[6]; /**< 40 * 6 = 240 */
203 VBOXHGCMSVCPARM aHostParms[6]; /**< 24 * 6 = 144 */
204} VBOXHGCMCMDCACHED; /**< 112+240+144 = 496 */
205AssertCompile(sizeof(VBOXHGCMCMD) <= 112);
206AssertCompile(sizeof(VBOXHGCMGUESTPARM) <= 40);
207AssertCompile(sizeof(VBOXHGCMSVCPARM) <= 24);
208AssertCompile(sizeof(VBOXHGCMCMDCACHED) <= 512);
209AssertCompile(sizeof(VBOXHGCMCMDCACHED) > sizeof(VBOXHGCMCMD) + sizeof(HGCMServiceLocation));
210
211
212static int vmmdevHGCMCmdListLock(PVMMDEV pThis)
213{
214 int rc = RTCritSectEnter(&pThis->critsectHGCMCmdList);
215 AssertRC(rc);
216 return rc;
217}
218
219static void vmmdevHGCMCmdListUnlock(PVMMDEV pThis)
220{
221 int rc = RTCritSectLeave(&pThis->critsectHGCMCmdList);
222 AssertRC(rc);
223}
224
225/** Allocate and initialize VBOXHGCMCMD structure for HGCM request.
226 *
227 * @returns Pointer to the command on success, NULL otherwise.
228 * @param pThis The VMMDev instance data.
229 * @param enmCmdType Type of the command.
230 * @param GCPhys The guest physical address of the HGCM request.
231 * @param cbRequest The size of the HGCM request.
232 * @param cParms Number of HGCM parameters for VBOXHGCMCMDTYPE_CALL command.
233 */
234static PVBOXHGCMCMD vmmdevHGCMCmdAlloc(PVMMDEV pThis, VBOXHGCMCMDTYPE enmCmdType, RTGCPHYS GCPhys, uint32_t cbRequest, uint32_t cParms)
235{
236#if 1
237 /*
238 * Try use the cache.
239 */
240 VBOXHGCMCMDCACHED *pCmdCached;
241 AssertCompile(sizeof(*pCmdCached) >= sizeof(VBOXHGCMCMD) + sizeof(HGCMServiceLocation));
242 if (cParms <= RT_ELEMENTS(pCmdCached->aGuestParms))
243 {
244 int rc = RTMemCacheAllocEx(pThis->hHgcmCmdCache, (void **)&pCmdCached);
245 if (RT_SUCCESS(rc))
246 {
247 RT_ZERO(*pCmdCached);
248 pCmdCached->Core.fMemCache = true;
249 pCmdCached->Core.GCPhys = GCPhys;
250 pCmdCached->Core.cbRequest = cbRequest;
251 pCmdCached->Core.enmCmdType = enmCmdType;
252 if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
253 {
254 pCmdCached->Core.u.call.cParms = cParms;
255 pCmdCached->Core.u.call.paGuestParms = pCmdCached->aGuestParms;
256 pCmdCached->Core.u.call.paHostParms = pCmdCached->aHostParms;
257 }
258 else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
259 pCmdCached->Core.u.connect.pLoc = (HGCMServiceLocation *)(&pCmdCached->Core + 1);
260
261 return &pCmdCached->Core;
262 }
263 return NULL;
264 }
265 STAM_REL_COUNTER_INC(&pThis->StatHgcmLargeCmdAllocs);
266
267#else
268 RT_NOREF(pThis);
269#endif
270
271 /* Size of required memory buffer. */
272 const uint32_t cbCmd = sizeof(VBOXHGCMCMD) + cParms * (sizeof(VBOXHGCMGUESTPARM) + sizeof(VBOXHGCMSVCPARM))
273 + (enmCmdType == VBOXHGCMCMDTYPE_CONNECT ? sizeof(HGCMServiceLocation) : 0);
274
275 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ(cbCmd);
276 if (pCmd)
277 {
278 pCmd->enmCmdType = enmCmdType;
279 pCmd->GCPhys = GCPhys;
280 pCmd->cbRequest = cbRequest;
281
282 if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
283 {
284 pCmd->u.call.cParms = cParms;
285 if (cParms)
286 {
287 pCmd->u.call.paGuestParms = (VBOXHGCMGUESTPARM *)((uint8_t *)pCmd
288 + sizeof(struct VBOXHGCMCMD));
289 pCmd->u.call.paHostParms = (VBOXHGCMSVCPARM *)((uint8_t *)pCmd->u.call.paGuestParms
290 + cParms * sizeof(VBOXHGCMGUESTPARM));
291 }
292 }
293 else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
294 pCmd->u.connect.pLoc = (HGCMServiceLocation *)(pCmd + 1);
295 }
296 return pCmd;
297}
298
299/** Deallocate VBOXHGCMCMD memory.
300 *
301 * @param pThis The VMMDev instance data.
302 * @param pCmd Command to deallocate.
303 */
304static void vmmdevHGCMCmdFree(PVMMDEV pThis, PVBOXHGCMCMD pCmd)
305{
306 if (pCmd)
307 {
308 if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL)
309 {
310 uint32_t i;
311 for (i = 0; i < pCmd->u.call.cParms; ++i)
312 {
313 VBOXHGCMSVCPARM * const pHostParm = &pCmd->u.call.paHostParms[i];
314 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
315
316 if (pHostParm->type == VBOX_HGCM_SVC_PARM_PTR)
317 RTMemFree(pHostParm->u.pointer.addr);
318
319 if ( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In
320 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out
321 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr
322 || pGuestParm->enmType == VMMDevHGCMParmType_PageList)
323 if (pGuestParm->u.ptr.paPages != &pGuestParm->u.ptr.GCPhysSinglePage)
324 RTMemFree(pGuestParm->u.ptr.paPages);
325 }
326 }
327
328 if (pCmd->pvReqLocked)
329 {
330 PDMDevHlpPhysReleasePageMappingLock(pThis->pDevIns, &pCmd->ReqMapLock);
331 pCmd->pvReqLocked = NULL;
332 }
333
334#if 1
335 if (pCmd->fMemCache)
336 RTMemCacheFree(pThis->hHgcmCmdCache, pCmd);
337 else
338#endif
339 RTMemFree(pCmd);
340 }
341}
342
343/** Add VBOXHGCMCMD to the list of pending commands.
344 *
345 * @returns VBox status code.
346 * @param pThis The VMMDev instance data.
347 * @param pCmd Command to add.
348 */
349static int vmmdevHGCMAddCommand(PVMMDEV pThis, PVBOXHGCMCMD pCmd)
350{
351 int rc = vmmdevHGCMCmdListLock(pThis);
352 AssertRCReturn(rc, rc);
353
354 LogFlowFunc(("%p type %d\n", pCmd, pCmd->enmCmdType));
355
356 RTListPrepend(&pThis->listHGCMCmd, &pCmd->node);
357
358 /* Automatically enable HGCM events, if there are HGCM commands. */
359 if ( pCmd->enmCmdType == VBOXHGCMCMDTYPE_CONNECT
360 || pCmd->enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT
361 || pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL)
362 {
363 LogFunc(("u32HGCMEnabled = %d\n", pThis->u32HGCMEnabled));
364 if (ASMAtomicCmpXchgU32(&pThis->u32HGCMEnabled, 1, 0))
365 VMMDevCtlSetGuestFilterMask(pThis, VMMDEV_EVENT_HGCM, 0);
366 }
367
368 vmmdevHGCMCmdListUnlock(pThis);
369 return rc;
370}
371
372/** Remove VBOXHGCMCMD from the list of pending commands.
373 *
374 * @returns VBox status code.
375 * @param pThis The VMMDev instance data.
376 * @param pCmd Command to remove.
377 */
378static int vmmdevHGCMRemoveCommand(PVMMDEV pThis, PVBOXHGCMCMD pCmd)
379{
380 int rc = vmmdevHGCMCmdListLock(pThis);
381 AssertRCReturn(rc, rc);
382
383 LogFlowFunc(("%p\n", pCmd));
384
385 RTListNodeRemove(&pCmd->node);
386
387 vmmdevHGCMCmdListUnlock(pThis);
388 return rc;
389}
390
391/**
392 * Find a HGCM command by its physical address.
393 *
394 * The caller is responsible for taking the command list lock before calling
395 * this function.
396 *
397 * @returns Pointer to the command on success, NULL otherwise.
398 * @param pThis The VMMDev instance data.
399 * @param GCPhys The physical address of the command we're looking for.
400 */
401DECLINLINE(PVBOXHGCMCMD) vmmdevHGCMFindCommandLocked(PVMMDEV pThis, RTGCPHYS GCPhys)
402{
403 PVBOXHGCMCMD pCmd;
404 RTListForEach(&pThis->listHGCMCmd, pCmd, VBOXHGCMCMD, node)
405 {
406 if (pCmd->GCPhys == GCPhys)
407 return pCmd;
408 }
409 return NULL;
410}
411
412/** Copy VMMDevHGCMConnect request data from the guest to VBOXHGCMCMD command.
413 *
414 * @param pHGCMConnect The source guest request (cached in host memory).
415 * @param pCmd Destination command.
416 */
417static void vmmdevHGCMConnectFetch(const VMMDevHGCMConnect *pHGCMConnect, PVBOXHGCMCMD pCmd)
418{
419 pCmd->enmRequestType = pHGCMConnect->header.header.requestType;
420 pCmd->u.connect.u32ClientID = pHGCMConnect->u32ClientID;
421 *pCmd->u.connect.pLoc = pHGCMConnect->loc;
422}
423
424/** Handle VMMDevHGCMConnect request.
425 *
426 * @param pThis The VMMDev instance data.
427 * @param pHGCMConnect The guest request (cached in host memory).
428 * @param GCPhys The physical address of the request.
429 */
430int vmmdevHGCMConnect(PVMMDEV pThis, const VMMDevHGCMConnect *pHGCMConnect, RTGCPHYS GCPhys)
431{
432 int rc = VINF_SUCCESS;
433
434 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_CONNECT, GCPhys, pHGCMConnect->header.header.size, 0);
435 if (pCmd)
436 {
437 vmmdevHGCMConnectFetch(pHGCMConnect, pCmd);
438
439 /* Only allow the guest to use existing services! */
440 ASSERT_GUEST(pHGCMConnect->loc.type == VMMDevHGCMLoc_LocalHost_Existing);
441 pCmd->u.connect.pLoc->type = VMMDevHGCMLoc_LocalHost_Existing;
442
443 vmmdevHGCMAddCommand(pThis, pCmd);
444 rc = pThis->pHGCMDrv->pfnConnect(pThis->pHGCMDrv, pCmd, pCmd->u.connect.pLoc, &pCmd->u.connect.u32ClientID);
445 if (RT_FAILURE(rc))
446 vmmdevHGCMRemoveCommand(pThis, pCmd);
447 }
448 else
449 {
450 rc = VERR_NO_MEMORY;
451 }
452
453 return rc;
454}
455
456/** Copy VMMDevHGCMDisconnect request data from the guest to VBOXHGCMCMD command.
457 *
458 * @param pHGCMDisconnect The source guest request (cached in host memory).
459 * @param pCmd Destination command.
460 */
461static void vmmdevHGCMDisconnectFetch(const VMMDevHGCMDisconnect *pHGCMDisconnect, PVBOXHGCMCMD pCmd)
462{
463 pCmd->enmRequestType = pHGCMDisconnect->header.header.requestType;
464 pCmd->u.disconnect.u32ClientID = pHGCMDisconnect->u32ClientID;
465}
466
467/** Handle VMMDevHGCMDisconnect request.
468 *
469 * @param pThis The VMMDev instance data.
470 * @param pHGCMDisconnect The guest request (cached in host memory).
471 * @param GCPhys The physical address of the request.
472 */
473int vmmdevHGCMDisconnect(PVMMDEV pThis, const VMMDevHGCMDisconnect *pHGCMDisconnect, RTGCPHYS GCPhys)
474{
475 int rc = VINF_SUCCESS;
476
477 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_DISCONNECT, GCPhys, pHGCMDisconnect->header.header.size, 0);
478 if (pCmd)
479 {
480 vmmdevHGCMDisconnectFetch(pHGCMDisconnect, pCmd);
481
482 vmmdevHGCMAddCommand(pThis, pCmd);
483 rc = pThis->pHGCMDrv->pfnDisconnect (pThis->pHGCMDrv, pCmd, pCmd->u.disconnect.u32ClientID);
484 if (RT_FAILURE(rc))
485 vmmdevHGCMRemoveCommand(pThis, pCmd);
486 }
487 else
488 rc = VERR_NO_MEMORY;
489
490 return rc;
491}
492
493/** Translate LinAddr parameter type to the direction of data transfer.
494 *
495 * @returns VBOX_HGCM_F_PARM_DIRECTION_* flags.
496 * @param enmType Type of the LinAddr parameter.
497 */
498static uint32_t vmmdevHGCMParmTypeToDirection(HGCMFunctionParameterType enmType)
499{
500 if (enmType == VMMDevHGCMParmType_LinAddr_In) return VBOX_HGCM_F_PARM_DIRECTION_TO_HOST;
501 if (enmType == VMMDevHGCMParmType_LinAddr_Out) return VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
502 return VBOX_HGCM_F_PARM_DIRECTION_BOTH;
503}
504
505/** Check if list of pages in a HGCM pointer parameter corresponds to a contiguous buffer.
506 *
507 * @returns true if pages are contiguous, false otherwise.
508 * @param pPtr Information about a pointer HGCM parameter.
509 */
510DECLINLINE(bool) vmmdevHGCMGuestBufferIsContiguous(const VBOXHGCMPARMPTR *pPtr)
511{
512 if (pPtr->cPages == 1)
513 return true;
514 RTGCPHYS64 Phys = pPtr->paPages[0] + PAGE_SIZE;
515 if (Phys != pPtr->paPages[1])
516 return false;
517 if (pPtr->cPages > 2)
518 {
519 uint32_t iPage = 2;
520 do
521 {
522 Phys += PAGE_SIZE;
523 if (Phys != pPtr->paPages[iPage])
524 return false;
525 ++iPage;
526 } while (iPage < pPtr->cPages);
527 }
528 return true;
529}
530
531/** Copy data from guest memory to the host buffer.
532 *
533 * @returns VBox status code.
534 * @param pDevIns The device instance for PDMDevHlp.
535 * @param pvDst The destination host buffer.
536 * @param cbDst Size of the destination host buffer.
537 * @param pPtr Description of the source HGCM pointer parameter.
538 */
539static int vmmdevHGCMGuestBufferRead(PPDMDEVINSR3 pDevIns, void *pvDst, uint32_t cbDst,
540 const VBOXHGCMPARMPTR *pPtr)
541{
542 /*
543 * Try detect contiguous buffers.
544 */
545 /** @todo We need a flag for indicating this. */
546 if (vmmdevHGCMGuestBufferIsContiguous(pPtr))
547 return PDMDevHlpPhysRead(pDevIns, pPtr->paPages[0] | pPtr->offFirstPage, pvDst, cbDst);
548
549 /*
550 * Page by page fallback.
551 */
552 uint8_t *pu8Dst = (uint8_t *)pvDst;
553 uint32_t offPage = pPtr->offFirstPage;
554 uint32_t cbRemaining = cbDst;
555
556 for (uint32_t iPage = 0; iPage < pPtr->cPages && cbRemaining > 0; ++iPage)
557 {
558 uint32_t cbToRead = PAGE_SIZE - offPage;
559 if (cbToRead > cbRemaining)
560 cbToRead = cbRemaining;
561
562 /* Skip invalid pages. */
563 const RTGCPHYS GCPhys = pPtr->paPages[iPage];
564 if (GCPhys != NIL_RTGCPHYS)
565 {
566 int rc = PDMDevHlpPhysRead(pDevIns, GCPhys + offPage, pu8Dst, cbToRead);
567 AssertMsgReturn(RT_SUCCESS(rc), ("rc=%Rrc GCPhys=%RGp offPage=%#x cbToRead=%#x\n", rc, GCPhys, offPage, cbToRead), rc);
568 }
569
570 offPage = 0; /* A next page is read from 0 offset. */
571 cbRemaining -= cbToRead;
572 pu8Dst += cbToRead;
573 }
574
575 return VINF_SUCCESS;
576}
577
578/** Copy data from the host buffer to guest memory.
579 *
580 * @returns VBox status code.
581 * @param pDevIns The device instance for PDMDevHlp.
582 * @param pPtr Description of the destination HGCM pointer parameter.
583 * @param pvSrc The source host buffer.
584 * @param cbSrc Size of the source host buffer.
585 */
586static int vmmdevHGCMGuestBufferWrite(PPDMDEVINSR3 pDevIns, const VBOXHGCMPARMPTR *pPtr,
587 const void *pvSrc, uint32_t cbSrc)
588{
589 int rc = VINF_SUCCESS;
590
591 uint8_t *pu8Src = (uint8_t *)pvSrc;
592 uint32_t offPage = pPtr->offFirstPage;
593 uint32_t cbRemaining = cbSrc;
594
595 uint32_t iPage;
596 for (iPage = 0; iPage < pPtr->cPages && cbRemaining > 0; ++iPage)
597 {
598 uint32_t cbToWrite = PAGE_SIZE - offPage;
599 if (cbToWrite > cbRemaining)
600 cbToWrite = cbRemaining;
601
602 /* Skip invalid pages. */
603 const RTGCPHYS GCPhys = pPtr->paPages[iPage];
604 if (GCPhys != NIL_RTGCPHYS)
605 {
606 rc = PDMDevHlpPhysWrite(pDevIns, GCPhys + offPage, pu8Src, cbToWrite);
607 AssertRCBreak(rc);
608 }
609
610 offPage = 0; /* A next page is written at 0 offset. */
611 cbRemaining -= cbToWrite;
612 pu8Src += cbToWrite;
613 }
614
615 return rc;
616}
617
618/** Initializes pCmd->paHostParms from already initialized pCmd->paGuestParms.
619 * Allocates memory for pointer parameters and copies data from the guest.
620 *
621 * @returns VBox status code that the guest should see.
622 * @param pThis The VMMDev instance data.
623 * @param pCmd Command structure where host parameters needs initialization.
624 * @param pbReq The request buffer.
625 */
626static int vmmdevHGCMInitHostParameters(PVMMDEV pThis, PVBOXHGCMCMD pCmd, uint8_t const *pbReq)
627{
628 AssertReturn(pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL, VERR_INTERNAL_ERROR);
629
630 for (uint32_t i = 0; i < pCmd->u.call.cParms; ++i)
631 {
632 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
633 VBOXHGCMSVCPARM * const pHostParm = &pCmd->u.call.paHostParms[i];
634
635 switch (pGuestParm->enmType)
636 {
637 case VMMDevHGCMParmType_32bit:
638 {
639 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
640 pHostParm->u.uint32 = (uint32_t)pGuestParm->u.val.u64Value;
641
642 break;
643 }
644
645 case VMMDevHGCMParmType_64bit:
646 {
647 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
648 pHostParm->u.uint64 = pGuestParm->u.val.u64Value;
649
650 break;
651 }
652
653 case VMMDevHGCMParmType_LinAddr_In:
654 case VMMDevHGCMParmType_LinAddr_Out:
655 case VMMDevHGCMParmType_LinAddr:
656 case VMMDevHGCMParmType_PageList:
657 case VMMDevHGCMParmType_Embedded:
658 {
659 const uint32_t cbData = pGuestParm->u.ptr.cbData;
660
661 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
662 pHostParm->u.pointer.size = cbData;
663
664 if (cbData)
665 {
666 /* Zero memory, the buffer content is potentially copied to the guest. */
667 void *pv = RTMemAllocZ(cbData);
668 AssertReturn(pv, VERR_NO_MEMORY);
669 pHostParm->u.pointer.addr = pv;
670
671 if (pGuestParm->u.ptr.fu32Direction & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
672 {
673 if (pGuestParm->enmType != VMMDevHGCMParmType_Embedded)
674 {
675 int rc = vmmdevHGCMGuestBufferRead(pThis->pDevIns, pv, cbData, &pGuestParm->u.ptr);
676 ASSERT_GUEST_RETURN(RT_SUCCESS(rc), rc);
677 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
678 }
679 else
680 {
681 memcpy(pv, &pbReq[pGuestParm->u.ptr.offFirstPage], cbData);
682 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
683 }
684 }
685 }
686 else
687 {
688 pHostParm->u.pointer.addr = NULL;
689 }
690
691 break;
692 }
693
694 default:
695 ASSERT_GUEST_FAILED_RETURN(VERR_INVALID_PARAMETER);
696 }
697 }
698
699 return VINF_SUCCESS;
700}
701
702
703/** Allocate and initialize VBOXHGCMCMD structure for a HGCMCall request.
704 *
705 * @returns VBox status code that the guest should see.
706 * @param pThis The VMMDev instance data.
707 * @param pHGCMCall The HGCMCall request (cached in host memory).
708 * @param cbHGCMCall Size of the request.
709 * @param GCPhys Guest physical address of the request.
710 * @param enmRequestType The request type. Distinguishes 64 and 32 bit calls.
711 * @param ppCmd Where to store pointer to allocated command.
712 * @param pcbHGCMParmStruct Where to store size of used HGCM parameter structure.
713 */
714static int vmmdevHGCMCallAlloc(PVMMDEV pThis, const VMMDevHGCMCall *pHGCMCall, uint32_t cbHGCMCall, RTGCPHYS GCPhys,
715 VMMDevRequestType enmRequestType, PVBOXHGCMCMD *ppCmd, uint32_t *pcbHGCMParmStruct)
716{
717#ifdef VBOX_WITH_64_BITS_GUESTS
718 const uint32_t cbHGCMParmStruct = enmRequestType == VMMDevReq_HGCMCall64 ? sizeof(HGCMFunctionParameter64)
719 : sizeof(HGCMFunctionParameter32);
720#else
721 const uint32_t cbHGCMParmStruct = sizeof(HGCMFunctionParameter);
722#endif
723
724 const uint32_t cParms = pHGCMCall->cParms;
725
726 /* Whether there is enough space for parameters and sane upper limit. */
727 ASSERT_GUEST_STMT_RETURN( cParms <= (cbHGCMCall - sizeof(VMMDevHGCMCall)) / cbHGCMParmStruct
728 && cParms <= VMMDEV_MAX_HGCM_PARMS,
729 LogRelMax(50, ("VMMDev: request packet with invalid number of HGCM parameters: %d vs %d. Refusing operation.\n",
730 (cbHGCMCall - sizeof(VMMDevHGCMCall)) / cbHGCMParmStruct, cParms)),
731 VERR_INVALID_PARAMETER);
732 RT_UNTRUSTED_VALIDATED_FENCE();
733
734 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_CALL, GCPhys, cbHGCMCall, cParms);
735 if (pCmd == NULL)
736 return VERR_NO_MEMORY;
737
738 /* Request type has been validated in vmmdevReqDispatcher. */
739 pCmd->enmRequestType = enmRequestType;
740 pCmd->u.call.u32ClientID = pHGCMCall->u32ClientID;
741 pCmd->u.call.u32Function = pHGCMCall->u32Function;
742
743 *ppCmd = pCmd;
744 *pcbHGCMParmStruct = cbHGCMParmStruct;
745 return VINF_SUCCESS;
746}
747
748/** Copy VMMDevHGCMCall request data from the guest to VBOXHGCMCMD command.
749 *
750 * @returns VBox status code that the guest should see.
751 * @param pThis The VMMDev instance data.
752 * @param pCmd The destination command.
753 * @param pHGCMCall The HGCMCall request (cached in host memory).
754 * @param cbHGCMCall Size of the request.
755 * @param enmRequestType The request type. Distinguishes 64 and 32 bit calls.
756 * @param cbHGCMParmStruct Size of used HGCM parameter structure.
757 */
758static int vmmdevHGCMCallFetchGuestParms(PVMMDEV pThis, PVBOXHGCMCMD pCmd,
759 const VMMDevHGCMCall *pHGCMCall, uint32_t cbHGCMCall,
760 VMMDevRequestType enmRequestType, uint32_t cbHGCMParmStruct)
761{
762 /*
763 * Go over all guest parameters and initialize relevant VBOXHGCMCMD fields.
764 * VBOXHGCMCMD must contain all information about the request,
765 * the request will be not read from the guest memory again.
766 */
767#ifdef VBOX_WITH_64_BITS_GUESTS
768 const bool f64Bits = (enmRequestType == VMMDevReq_HGCMCall64);
769#endif
770
771 const uint32_t cParms = pCmd->u.call.cParms;
772
773 /* Offsets in the request buffer to HGCM parameters and additional data. */
774 const uint32_t offHGCMParms = sizeof(VMMDevHGCMCall);
775 const uint32_t offExtra = offHGCMParms + cParms * cbHGCMParmStruct;
776
777 /* Pointer to the next HGCM parameter of the request. */
778 const uint8_t *pu8HGCMParm = (uint8_t *)pHGCMCall + offHGCMParms;
779
780 uint32_t cbTotalData = 0;
781 for (uint32_t i = 0; i < cParms; ++i, pu8HGCMParm += cbHGCMParmStruct)
782 {
783 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
784
785#ifdef VBOX_WITH_64_BITS_GUESTS
786 AssertCompileMembersSameSizeAndOffset(HGCMFunctionParameter64, type, HGCMFunctionParameter32, type);
787 pGuestParm->enmType = ((HGCMFunctionParameter64 *)pu8HGCMParm)->type;
788#else
789 pGuestParm->enmType = ((HGCMFunctionParameter *)pu8HGCMParm)->type;
790#endif
791
792 switch (pGuestParm->enmType)
793 {
794 case VMMDevHGCMParmType_32bit:
795 {
796#ifdef VBOX_WITH_64_BITS_GUESTS
797 AssertCompileMembersSameSizeAndOffset(HGCMFunctionParameter64, u.value32, HGCMFunctionParameter32, u.value32);
798 uint32_t *pu32 = &((HGCMFunctionParameter64 *)pu8HGCMParm)->u.value32;
799#else
800 uint32_t *pu32 = &((HGCMFunctionParameter *)pu8HGCMParm)->u.value32;
801#endif
802 LogFunc(("uint32 guest parameter %RI32\n", *pu32));
803
804 pGuestParm->u.val.u64Value = *pu32;
805 pGuestParm->u.val.offValue = (uint32_t)((uintptr_t)pu32 - (uintptr_t)pHGCMCall);
806 pGuestParm->u.val.cbValue = sizeof(uint32_t);
807
808 break;
809 }
810
811 case VMMDevHGCMParmType_64bit:
812 {
813#ifdef VBOX_WITH_64_BITS_GUESTS
814 AssertCompileMembersSameSizeAndOffset(HGCMFunctionParameter64, u.value64, HGCMFunctionParameter32, u.value64);
815 uint64_t *pu64 = (uint64_t *)(uintptr_t)&((HGCMFunctionParameter64 *)pu8HGCMParm)->u.value64; /* MSC detect misalignment, thus casts. */
816#else
817 uint64_t *pu64 = &((HGCMFunctionParameter *)pu8HGCMParm)->u.value64;
818#endif
819 LogFunc(("uint64 guest parameter %RI64\n", *pu64));
820
821 pGuestParm->u.val.u64Value = *pu64;
822 pGuestParm->u.val.offValue = (uint32_t)((uintptr_t)pu64 - (uintptr_t)pHGCMCall);
823 pGuestParm->u.val.cbValue = sizeof(uint64_t);
824
825 break;
826 }
827
828 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
829 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
830 case VMMDevHGCMParmType_LinAddr: /* In & Out */
831 {
832#ifdef VBOX_WITH_64_BITS_GUESTS
833 uint32_t cbData = f64Bits ? ((HGCMFunctionParameter64 *)pu8HGCMParm)->u.Pointer.size
834 : ((HGCMFunctionParameter32 *)pu8HGCMParm)->u.Pointer.size;
835 RTGCPTR GCPtr = f64Bits ? ((HGCMFunctionParameter64 *)pu8HGCMParm)->u.Pointer.u.linearAddr
836 : ((HGCMFunctionParameter32 *)pu8HGCMParm)->u.Pointer.u.linearAddr;
837#else
838 uint32_t cbData = ((HGCMFunctionParameter *)pu8HGCMParm)->u.Pointer.size;
839 RTGCPTR GCPtr = ((HGCMFunctionParameter *)pu8HGCMParm)->u.Pointer.u.linearAddr;
840#endif
841 LogFunc(("LinAddr guest parameter %RGv, cb %u\n", GCPtr, cbData));
842
843 ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE - cbTotalData, VERR_INVALID_PARAMETER);
844 cbTotalData += cbData;
845
846 const uint32_t offFirstPage = cbData > 0 ? GCPtr & PAGE_OFFSET_MASK : 0;
847 const uint32_t cPages = cbData > 0 ? (offFirstPage + cbData + PAGE_SIZE - 1) / PAGE_SIZE : 0;
848
849 pGuestParm->u.ptr.cbData = cbData;
850 pGuestParm->u.ptr.offFirstPage = offFirstPage;
851 pGuestParm->u.ptr.cPages = cPages;
852 pGuestParm->u.ptr.fu32Direction = vmmdevHGCMParmTypeToDirection(pGuestParm->enmType);
853
854 if (cbData > 0)
855 {
856 if (cPages == 1)
857 pGuestParm->u.ptr.paPages = &pGuestParm->u.ptr.GCPhysSinglePage;
858 else
859 {
860 pGuestParm->u.ptr.paPages = (RTGCPHYS *)RTMemAlloc(cPages * sizeof(RTGCPHYS));
861 AssertReturn(pGuestParm->u.ptr.paPages, VERR_NO_MEMORY);
862 }
863
864 /* Gonvert the guest linear pointers of pages to physical addresses. */
865 GCPtr &= PAGE_BASE_GC_MASK;
866 for (uint32_t iPage = 0; iPage < cPages; ++iPage)
867 {
868 /* The guest might specify invalid GCPtr, just skip such addresses.
869 * Also if the guest parameters are fetched when restoring an old saved state,
870 * then GCPtr may become invalid and do not have a corresponding GCPhys.
871 * The command restoration routine will take care of this.
872 */
873 RTGCPHYS GCPhys;
874 int rc2 = PDMDevHlpPhysGCPtr2GCPhys(pThis->pDevIns, GCPtr, &GCPhys);
875 if (RT_FAILURE(rc2))
876 GCPhys = NIL_RTGCPHYS;
877 LogFunc(("Page %d: %RGv -> %RGp. %Rrc\n", iPage, GCPtr, GCPhys, rc2));
878
879 pGuestParm->u.ptr.paPages[iPage] = GCPhys;
880 GCPtr += PAGE_SIZE;
881 }
882 }
883
884 break;
885 }
886
887 case VMMDevHGCMParmType_PageList:
888 {
889#ifdef VBOX_WITH_64_BITS_GUESTS
890 AssertCompileMembersSameSizeAndOffset(HGCMFunctionParameter64, u.PageList.size, HGCMFunctionParameter32, u.PageList.size);
891 AssertCompileMembersSameSizeAndOffset(HGCMFunctionParameter64, u.PageList.offset, HGCMFunctionParameter32, u.PageList.offset);
892 uint32_t cbData = ((HGCMFunctionParameter64 *)pu8HGCMParm)->u.PageList.size;
893 uint32_t offPageListInfo = ((HGCMFunctionParameter64 *)pu8HGCMParm)->u.PageList.offset;
894#else
895 uint32_t cbData = ((HGCMFunctionParameter *)pu8HGCMParm)->u.PageList.size;
896 uint32_t offPageListInfo = ((HGCMFunctionParameter *)pu8HGCMParm)->u.PageList.offset;
897#endif
898 LogFunc(("PageList guest parameter cb %u, offset %u\n", cbData, offPageListInfo));
899
900 ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE - cbTotalData, VERR_INVALID_PARAMETER);
901 cbTotalData += cbData;
902
903/** @todo respect zero byte page lists... */
904 /* Check that the page list info is within the request. */
905 ASSERT_GUEST_RETURN( offPageListInfo >= offExtra
906 && cbHGCMCall >= sizeof(HGCMPageListInfo)
907 && offPageListInfo <= cbHGCMCall - sizeof(HGCMPageListInfo),
908 VERR_INVALID_PARAMETER);
909 RT_UNTRUSTED_VALIDATED_FENCE();
910
911 /* The HGCMPageListInfo structure is within the request. */
912 const HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + offPageListInfo);
913
914 /* Enough space for page pointers? */
915 const uint32_t cMaxPages = 1 + (cbHGCMCall - offPageListInfo - sizeof(HGCMPageListInfo)) / sizeof(RTGCPHYS);
916 ASSERT_GUEST_RETURN( pPageListInfo->cPages > 0
917 && pPageListInfo->cPages <= cMaxPages,
918 VERR_INVALID_PARAMETER);
919
920 /* Other fields of PageListInfo. */
921 ASSERT_GUEST_RETURN( (pPageListInfo->flags & ~VBOX_HGCM_F_PARM_DIRECTION_BOTH) == 0
922 && pPageListInfo->offFirstPage < PAGE_SIZE,
923 VERR_INVALID_PARAMETER);
924 RT_UNTRUSTED_VALIDATED_FENCE();
925
926 /* cbData is not checked to fit into the pages, because the host code does not access
927 * more than the provided number of pages.
928 */
929
930 pGuestParm->u.ptr.cbData = cbData;
931 pGuestParm->u.ptr.offFirstPage = pPageListInfo->offFirstPage;
932 pGuestParm->u.ptr.cPages = pPageListInfo->cPages;
933 pGuestParm->u.ptr.fu32Direction = pPageListInfo->flags;
934 if (pPageListInfo->cPages == 1)
935 {
936 pGuestParm->u.ptr.paPages = &pGuestParm->u.ptr.GCPhysSinglePage;
937 pGuestParm->u.ptr.GCPhysSinglePage = pPageListInfo->aPages[0];
938 }
939 else
940 {
941 pGuestParm->u.ptr.paPages = (RTGCPHYS *)RTMemAlloc(pPageListInfo->cPages * sizeof(RTGCPHYS));
942 AssertReturn(pGuestParm->u.ptr.paPages, VERR_NO_MEMORY);
943
944 for (uint32_t iPage = 0; iPage < pGuestParm->u.ptr.cPages; ++iPage)
945 pGuestParm->u.ptr.paPages[iPage] = pPageListInfo->aPages[iPage];
946 }
947 break;
948 }
949
950 case VMMDevHGCMParmType_Embedded:
951 {
952#ifdef VBOX_WITH_64_BITS_GUESTS
953 AssertCompileMembersSameSizeAndOffset(HGCMFunctionParameter64, u.Embedded.cbData, HGCMFunctionParameter32, u.Embedded.cbData);
954 uint32_t const cbData = ((HGCMFunctionParameter64 *)pu8HGCMParm)->u.Embedded.cbData;
955 uint32_t const offData = ((HGCMFunctionParameter64 *)pu8HGCMParm)->u.Embedded.offData;
956 uint32_t const fFlags = ((HGCMFunctionParameter64 *)pu8HGCMParm)->u.Embedded.fFlags;
957#else
958 uint32_t const cbData = ((HGCMFunctionParameter *)pu8HGCMParm)->u.Embedded.cbData;
959 uint32_t const offData = ((HGCMFunctionParameter *)pu8HGCMParm)->u.Embedded.offData;
960 uint32_t const fFlags = ((HGCMFunctionParameter *)pu8HGCMParm)->u.Embedded.fFlags;
961#endif
962 LogFunc(("Embedded guest parameter cb %u, offset %u, flags %#x\n", cbData, offData, fFlags));
963
964 ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE - cbTotalData, VERR_INVALID_PARAMETER);
965 cbTotalData += cbData;
966
967 /* Check flags and buffer range. */
968 ASSERT_GUEST_MSG_RETURN(VBOX_HGCM_F_PARM_ARE_VALID(fFlags), ("%#x\n", fFlags), VERR_INVALID_FLAGS);
969 ASSERT_GUEST_MSG_RETURN( offData >= offExtra
970 && offData <= cbHGCMCall
971 && cbData <= cbHGCMCall - offData,
972 ("offData=%#x cbData=%#x cbHGCMCall=%#x offExtra=%#x\n", offData, cbData, cbHGCMCall, offExtra),
973 VERR_INVALID_PARAMETER);
974 RT_UNTRUSTED_VALIDATED_FENCE();
975
976 /* We use part of the ptr member. */
977 pGuestParm->u.ptr.fu32Direction = fFlags;
978 pGuestParm->u.ptr.cbData = cbData;
979 pGuestParm->u.ptr.offFirstPage = offData;
980 pGuestParm->u.ptr.GCPhysSinglePage = pCmd->GCPhys + offData;
981 pGuestParm->u.ptr.cPages = 1;
982 pGuestParm->u.ptr.paPages = &pGuestParm->u.ptr.GCPhysSinglePage;
983 break;
984 }
985
986 default:
987 ASSERT_GUEST_FAILED_RETURN(VERR_INVALID_PARAMETER);
988 }
989 }
990
991 return VINF_SUCCESS;
992}
993
994/**
995 * Handles VMMDevHGCMCall request.
996 *
997 * @returns VBox status code that the guest should see.
998 * @param pThis The VMMDev instance data.
999 * @param pHGCMCall The request to handle (cached in host memory).
1000 * @param cbHGCMCall Size of the entire request (including HGCM parameters).
1001 * @param GCPhys The guest physical address of the request.
1002 * @param enmRequestType The request type. Distinguishes 64 and 32 bit calls.
1003 * @param tsArrival The STAM_GET_TS() value when the request arrived.
1004 * @param ppLock Pointer to the lock info pointer (latter can be
1005 * NULL). Set to NULL if HGCM takes lock ownership.
1006 */
1007int vmmdevHGCMCall(PVMMDEV pThis, const VMMDevHGCMCall *pHGCMCall, uint32_t cbHGCMCall, RTGCPHYS GCPhys,
1008 VMMDevRequestType enmRequestType, uint64_t tsArrival, PVMMDEVREQLOCK *ppLock)
1009{
1010 LogFunc(("client id = %d, function = %d, cParms = %d, enmRequestType = %d\n",
1011 pHGCMCall->u32ClientID, pHGCMCall->u32Function, pHGCMCall->cParms, enmRequestType));
1012
1013 /*
1014 * Validation.
1015 */
1016 ASSERT_GUEST_RETURN(cbHGCMCall >= sizeof(VMMDevHGCMCall), VERR_INVALID_PARAMETER);
1017#ifdef VBOX_WITH_64_BITS_GUESTS
1018 ASSERT_GUEST_RETURN( enmRequestType == VMMDevReq_HGCMCall32
1019 || enmRequestType == VMMDevReq_HGCMCall64, VERR_INVALID_PARAMETER);
1020#else
1021 ASSERT_GUEST_RETURN(enmRequestType == VMMDevReq_HGCMCall, VERR_INVALID_PARAMETER);
1022#endif
1023 RT_UNTRUSTED_VALIDATED_FENCE();
1024
1025 /*
1026 * Create a command structure.
1027 */
1028 PVBOXHGCMCMD pCmd;
1029 uint32_t cbHGCMParmStruct;
1030 int rc = vmmdevHGCMCallAlloc(pThis, pHGCMCall, cbHGCMCall, GCPhys, enmRequestType, &pCmd, &cbHGCMParmStruct);
1031 if (RT_SUCCESS(rc))
1032 {
1033 pCmd->tsArrival = tsArrival;
1034 PVMMDEVREQLOCK pLock = *ppLock;
1035 if (pLock)
1036 {
1037 pCmd->ReqMapLock = pLock->Lock;
1038 pCmd->pvReqLocked = pLock->pvReq;
1039 *ppLock = NULL;
1040 }
1041
1042 rc = vmmdevHGCMCallFetchGuestParms(pThis, pCmd, pHGCMCall, cbHGCMCall, enmRequestType, cbHGCMParmStruct);
1043 if (RT_SUCCESS(rc))
1044 {
1045 /* Copy guest data to host parameters, so HGCM services can use the data. */
1046 rc = vmmdevHGCMInitHostParameters(pThis, pCmd, (uint8_t const *)pHGCMCall);
1047 if (RT_SUCCESS(rc))
1048 {
1049 /*
1050 * Pass the function call to HGCM connector for actual processing
1051 */
1052 vmmdevHGCMAddCommand(pThis, pCmd);
1053
1054#if 0 /* DONT ENABLE - for performance hacking. */
1055 if ( pCmd->u.call.u32Function == 9
1056 && pCmd->u.call.cParms == 5)
1057 {
1058 vmmdevHGCMRemoveCommand(pThis, pCmd);
1059
1060 if (pCmd->pvReqLocked)
1061 {
1062 VMMDevHGCMRequestHeader volatile *pHeader = (VMMDevHGCMRequestHeader volatile *)pCmd->pvReqLocked;
1063 pHeader->header.rc = VINF_SUCCESS;
1064 pHeader->result = VINF_SUCCESS;
1065 pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
1066 }
1067 else
1068 {
1069 VMMDevHGCMRequestHeader *pHeader = (VMMDevHGCMRequestHeader *)pHGCMCall;
1070 pHeader->header.rc = VINF_SUCCESS;
1071 pHeader->result = VINF_SUCCESS;
1072 pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
1073 PDMDevHlpPhysWrite(pThis->pDevIns, GCPhys, pHeader, sizeof(*pHeader));
1074 }
1075 vmmdevHGCMCmdFree(pThis, pCmd);
1076 return VINF_HGCM_ASYNC_EXECUTE; /* ignored, but avoids assertions. */
1077 }
1078#endif
1079
1080 rc = pThis->pHGCMDrv->pfnCall(pThis->pHGCMDrv, pCmd,
1081 pCmd->u.call.u32ClientID, pCmd->u.call.u32Function,
1082 pCmd->u.call.cParms, pCmd->u.call.paHostParms, tsArrival);
1083
1084 if (rc == VINF_HGCM_ASYNC_EXECUTE)
1085 {
1086 /*
1087 * Done. Just update statistics and return.
1088 */
1089#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
1090 uint64_t tsNow;
1091 STAM_GET_TS(tsNow);
1092 STAM_REL_PROFILE_ADD_PERIOD(&pThis->StatHgcmCmdArrival, tsNow - tsArrival);
1093#endif
1094 return rc;
1095 }
1096
1097 /*
1098 * Failed, bail out.
1099 */
1100 LogFunc(("pfnCall rc = %Rrc\n", rc));
1101 vmmdevHGCMRemoveCommand(pThis, pCmd);
1102 }
1103 }
1104 vmmdevHGCMCmdFree(pThis, pCmd);
1105 }
1106 return rc;
1107}
1108
1109/**
1110 * VMMDevReq_HGCMCancel worker.
1111 *
1112 * @returns VBox status code that the guest should see.
1113 * @param pThis The VMMDev instance data.
1114 * @param pHGCMCancel The request to handle (cached in host memory).
1115 * @param GCPhys The address of the request.
1116 *
1117 * @thread EMT
1118 */
1119int vmmdevHGCMCancel(PVMMDEV pThis, const VMMDevHGCMCancel *pHGCMCancel, RTGCPHYS GCPhys)
1120{
1121 NOREF(pHGCMCancel);
1122 int rc = vmmdevHGCMCancel2(pThis, GCPhys);
1123 return rc == VERR_NOT_FOUND ? VERR_INVALID_PARAMETER : rc;
1124}
1125
1126/**
1127 * VMMDevReq_HGCMCancel2 worker.
1128 *
1129 * @retval VINF_SUCCESS on success.
1130 * @retval VERR_NOT_FOUND if the request was not found.
1131 * @retval VERR_INVALID_PARAMETER if the request address is invalid.
1132 *
1133 * @param pThis The VMMDev instance data.
1134 * @param GCPhys The address of the request that should be cancelled.
1135 *
1136 * @thread EMT
1137 */
1138int vmmdevHGCMCancel2(PVMMDEV pThis, RTGCPHYS GCPhys)
1139{
1140 if ( GCPhys == 0
1141 || GCPhys == NIL_RTGCPHYS
1142 || GCPhys == NIL_RTGCPHYS32)
1143 {
1144 Log(("vmmdevHGCMCancel2: GCPhys=%#x\n", GCPhys));
1145 return VERR_INVALID_PARAMETER;
1146 }
1147
1148 /*
1149 * Locate the command and cancel it while under the protection of
1150 * the lock. hgcmCompletedWorker makes assumptions about this.
1151 */
1152 int rc = vmmdevHGCMCmdListLock(pThis);
1153 AssertRCReturn(rc, rc);
1154
1155 PVBOXHGCMCMD pCmd = vmmdevHGCMFindCommandLocked(pThis, GCPhys);
1156 if (pCmd)
1157 {
1158 pCmd->fCancelled = true;
1159 Log(("vmmdevHGCMCancel2: Cancelled pCmd=%p / GCPhys=%#x\n", pCmd, GCPhys));
1160 }
1161 else
1162 rc = VERR_NOT_FOUND;
1163
1164 vmmdevHGCMCmdListUnlock(pThis);
1165 return rc;
1166}
1167
1168/** Write HGCM call parameters and buffers back to the guest request and memory.
1169 *
1170 * @returns VBox status code that the guest should see.
1171 * @param pThis The VMMDev instance data.
1172 * @param pCmd Completed call command.
1173 * @param pHGCMCall The guestrequest which needs updating (cached in the host memory).
1174 */
1175static int vmmdevHGCMCompleteCallRequest(PVMMDEV pThis, PVBOXHGCMCMD pCmd, VMMDevHGCMCall *pHGCMCall)
1176{
1177 AssertReturn(pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL, VERR_INTERNAL_ERROR);
1178
1179 int rc = VINF_SUCCESS;
1180
1181 /*
1182 * Go over parameter descriptions saved in pCmd.
1183 */
1184 uint32_t i;
1185 for (i = 0; i < pCmd->u.call.cParms; ++i)
1186 {
1187 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
1188 VBOXHGCMSVCPARM * const pHostParm = &pCmd->u.call.paHostParms[i];
1189
1190 const HGCMFunctionParameterType enmType = pGuestParm->enmType;
1191 switch (enmType)
1192 {
1193 case VMMDevHGCMParmType_32bit:
1194 case VMMDevHGCMParmType_64bit:
1195 {
1196 const VBOXHGCMPARMVAL * const pVal = &pGuestParm->u.val;
1197 const void *pvSrc = enmType == VMMDevHGCMParmType_32bit ? (void *)&pHostParm->u.uint32
1198 : (void *)&pHostParm->u.uint64;
1199 memcpy((uint8_t *)pHGCMCall + pVal->offValue, pvSrc, pVal->cbValue);
1200 break;
1201 }
1202
1203 case VMMDevHGCMParmType_LinAddr_In:
1204 case VMMDevHGCMParmType_LinAddr_Out:
1205 case VMMDevHGCMParmType_LinAddr:
1206 case VMMDevHGCMParmType_PageList:
1207 {
1208 const VBOXHGCMPARMPTR * const pPtr = &pGuestParm->u.ptr;
1209 if ( pPtr->cbData > 0
1210 && pPtr->fu32Direction & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
1211 {
1212 const void *pvSrc = pHostParm->u.pointer.addr;
1213 uint32_t cbSrc = pHostParm->u.pointer.size;
1214 rc = vmmdevHGCMGuestBufferWrite(pThis->pDevIns, pPtr, pvSrc, cbSrc);
1215 }
1216 break;
1217 }
1218
1219 case VMMDevHGCMParmType_Embedded:
1220 {
1221 const VBOXHGCMPARMPTR * const pPtr = &pGuestParm->u.ptr;
1222 if ( pPtr->cbData > 0
1223 && (pPtr->fu32Direction & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST))
1224 {
1225 const void *pvSrc = pHostParm->u.pointer.addr;
1226 uint32_t cbSrc = pHostParm->u.pointer.size;
1227 if (pCmd->pvReqLocked)
1228 memcpy((uint8_t *)pCmd->pvReqLocked + pPtr->offFirstPage, pvSrc, cbSrc);
1229 else
1230 rc = PDMDevHlpPhysWrite(pThis->pDevIns, pGuestParm->u.ptr.GCPhysSinglePage, pvSrc, cbSrc);
1231 }
1232 break;
1233 }
1234
1235 default:
1236 break;
1237 }
1238
1239 if (RT_FAILURE(rc))
1240 break;
1241 }
1242
1243 return rc;
1244}
1245
1246/** Update HGCM request in the guest memory and mark it as completed.
1247 *
1248 * @param pInterface Pointer to this PDM interface.
1249 * @param result HGCM completion status code (VBox status code).
1250 * @param pCmd Completed command, which contains updated host parameters.
1251 *
1252 * @thread EMT
1253 */
1254DECLCALLBACK(void) hgcmCompletedWorker(PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
1255{
1256 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1257#ifdef VBOX_WITH_DTRACE
1258 uint32_t idFunction = 0;
1259 uint32_t idClient = 0;
1260#endif
1261
1262 if (result == VINF_HGCM_SAVE_STATE)
1263 {
1264 /* If the completion routine was called while the HGCM service saves its state,
1265 * then currently nothing to be done here. The pCmd stays in the list and will
1266 * be saved later when the VMMDev state will be saved and re-submitted on load.
1267 *
1268 * It it assumed that VMMDev saves state after the HGCM services (VMMDev driver
1269 * attached by constructor before it registers its SSM state), and, therefore,
1270 * VBOXHGCMCMD structures are not removed by vmmdevHGCMSaveState from the list,
1271 * while HGCM uses them.
1272 */
1273 LogFlowFunc(("VINF_HGCM_SAVE_STATE for command %p\n", pCmd));
1274 return;
1275 }
1276
1277 VBOXDD_HGCMCALL_COMPLETED_EMT(pCmd, result);
1278
1279 int rc = VINF_SUCCESS;
1280
1281 /*
1282 * The cancellation protocol requires us to remove the command here
1283 * and then check the flag. Cancelled commands must not be written
1284 * back to guest memory.
1285 */
1286 vmmdevHGCMRemoveCommand(pThis, pCmd);
1287
1288 if (RT_LIKELY(!pCmd->fCancelled))
1289 {
1290 if (!pCmd->pvReqLocked)
1291 {
1292 /*
1293 * Request is not locked:
1294 */
1295 VMMDevHGCMRequestHeader *pHeader = (VMMDevHGCMRequestHeader *)RTMemAlloc(pCmd->cbRequest);
1296 if (pHeader)
1297 {
1298 /*
1299 * Read the request from the guest memory for updating.
1300 * The request data is not be used for anything but checking the request type.
1301 */
1302 PDMDevHlpPhysRead(pThis->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbRequest);
1303 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
1304
1305 /* Verify the request type. This is the only field which is used from the guest memory. */
1306 const VMMDevRequestType enmRequestType = pHeader->header.requestType;
1307 if ( enmRequestType == pCmd->enmRequestType
1308 || enmRequestType == VMMDevReq_HGCMCancel)
1309 {
1310 RT_UNTRUSTED_VALIDATED_FENCE();
1311
1312 /*
1313 * Update parameters and data buffers.
1314 */
1315 switch (enmRequestType)
1316 {
1317#ifdef VBOX_WITH_64_BITS_GUESTS
1318 case VMMDevReq_HGCMCall64:
1319 case VMMDevReq_HGCMCall32:
1320#else
1321 case VMMDevReq_HGCMCall:
1322#endif
1323 {
1324 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
1325 rc = vmmdevHGCMCompleteCallRequest(pThis, pCmd, pHGCMCall);
1326#ifdef VBOX_WITH_DTRACE
1327 idFunction = pCmd->u.call.u32Function;
1328 idClient = pCmd->u.call.u32ClientID;
1329#endif
1330 break;
1331 }
1332
1333 case VMMDevReq_HGCMConnect:
1334 {
1335 /* save the client id in the guest request packet */
1336 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)pHeader;
1337 pHGCMConnect->u32ClientID = pCmd->u.connect.u32ClientID;
1338 break;
1339 }
1340
1341 default:
1342 /* make compiler happy */
1343 break;
1344 }
1345 }
1346 else
1347 {
1348 /* Guest has changed the command type. */
1349 LogRelMax(50, ("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n",
1350 pCmd->enmCmdType, pHeader->header.requestType));
1351
1352 ASSERT_GUEST_FAILED_STMT(rc = VERR_INVALID_PARAMETER);
1353 }
1354
1355 /* Setup return code for the guest. */
1356 if (RT_SUCCESS(rc))
1357 pHeader->result = result;
1358 else
1359 pHeader->result = rc;
1360
1361 /* First write back the request. */
1362 PDMDevHlpPhysWrite(pThis->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbRequest);
1363
1364 /* Mark request as processed. */
1365 pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
1366
1367 /* Second write the flags to mark the request as processed. */
1368 PDMDevHlpPhysWrite(pThis->pDevIns, pCmd->GCPhys + RT_UOFFSETOF(VMMDevHGCMRequestHeader, fu32Flags),
1369 &pHeader->fu32Flags, sizeof(pHeader->fu32Flags));
1370
1371 /* Now, when the command was removed from the internal list, notify the guest. */
1372 VMMDevNotifyGuest(pThis, VMMDEV_EVENT_HGCM);
1373
1374 RTMemFree(pHeader);
1375 }
1376 else
1377 {
1378 LogRelMax(10, ("VMMDev: Failed to allocate %u bytes for HGCM request completion!!!\n", pCmd->cbRequest));
1379 }
1380 }
1381 /*
1382 * Request was locked:
1383 */
1384 else
1385 {
1386 VMMDevHGCMRequestHeader volatile *pHeader = (VMMDevHGCMRequestHeader volatile *)pCmd->pvReqLocked;
1387
1388 /* Verify the request type. This is the only field which is used from the guest memory. */
1389 const VMMDevRequestType enmRequestType = pHeader->header.requestType;
1390 if ( enmRequestType == pCmd->enmRequestType
1391 || enmRequestType == VMMDevReq_HGCMCancel)
1392 {
1393 RT_UNTRUSTED_VALIDATED_FENCE();
1394
1395 /*
1396 * Update parameters and data buffers.
1397 */
1398 switch (enmRequestType)
1399 {
1400#ifdef VBOX_WITH_64_BITS_GUESTS
1401 case VMMDevReq_HGCMCall64:
1402 case VMMDevReq_HGCMCall32:
1403#else
1404 case VMMDevReq_HGCMCall:
1405#endif
1406 {
1407 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
1408 rc = vmmdevHGCMCompleteCallRequest(pThis, pCmd, pHGCMCall);
1409#ifdef VBOX_WITH_DTRACE
1410 idFunction = pCmd->u.call.u32Function;
1411 idClient = pCmd->u.call.u32ClientID;
1412#endif
1413 break;
1414 }
1415
1416 case VMMDevReq_HGCMConnect:
1417 {
1418 /* save the client id in the guest request packet */
1419 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)pHeader;
1420 pHGCMConnect->u32ClientID = pCmd->u.connect.u32ClientID;
1421 break;
1422 }
1423
1424 default:
1425 /* make compiler happy */
1426 break;
1427 }
1428 }
1429 else
1430 {
1431 /* Guest has changed the command type. */
1432 LogRelMax(50, ("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n",
1433 pCmd->enmCmdType, pHeader->header.requestType));
1434
1435 ASSERT_GUEST_FAILED_STMT(rc = VERR_INVALID_PARAMETER);
1436 }
1437
1438 /* Setup return code for the guest. */
1439 if (RT_SUCCESS(rc))
1440 pHeader->result = result;
1441 else
1442 pHeader->result = rc;
1443
1444 /* Mark request as processed. */
1445 ASMAtomicOrU32(&pHeader->fu32Flags, VBOX_HGCM_REQ_DONE);
1446
1447 /* Now, when the command was removed from the internal list, notify the guest. */
1448 VMMDevNotifyGuest(pThis, VMMDEV_EVENT_HGCM);
1449 }
1450 }
1451 else
1452 {
1453 LogFlowFunc(("Cancelled command %p\n", pCmd));
1454 }
1455
1456#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
1457 /* Save for final stats. */
1458 uint64_t const tsArrival = pCmd->tsArrival;
1459 uint64_t const tsComplete = pCmd->tsComplete;
1460#endif
1461
1462 /* Deallocate the command memory. */
1463 VBOXDD_HGCMCALL_COMPLETED_DONE(pCmd, idFunction, idClient, result);
1464 vmmdevHGCMCmdFree(pThis, pCmd);
1465
1466#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
1467 /* Update stats. */
1468 uint64_t tsNow;
1469 STAM_GET_TS(tsNow);
1470 STAM_REL_PROFILE_ADD_PERIOD(&pThis->StatHgcmCmdCompletion, tsNow - tsComplete);
1471 if (tsArrival != 0)
1472 STAM_REL_PROFILE_ADD_PERIOD(&pThis->StatHgcmCmdTotal, tsNow - tsArrival);
1473#endif
1474}
1475
1476/** HGCM callback for request completion. Forwards to hgcmCompletedWorker.
1477 *
1478 * @param pInterface Pointer to this PDM interface.
1479 * @param result HGCM completion status code (VBox status code).
1480 * @param pCmd Completed command, which contains updated host parameters.
1481 */
1482DECLCALLBACK(void) hgcmCompleted(PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
1483{
1484#if 0 /* This seems to be significantly slower. Half of MsgTotal time seems to be spend here. */
1485 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1486 STAM_GET_TS(pCmd->tsComplete);
1487
1488 VBOXDD_HGCMCALL_COMPLETED_REQ(pCmd, result);
1489
1490/** @todo no longer necessary to forward to EMT, but it might be more
1491 * efficient...? */
1492 /* Not safe to execute asynchronously; forward to EMT */
1493 int rc = VMR3ReqCallVoidNoWait(PDMDevHlpGetVM(pThis->pDevIns), VMCPUID_ANY,
1494 (PFNRT)hgcmCompletedWorker, 3, pInterface, result, pCmd);
1495 AssertRC(rc);
1496#else
1497 STAM_GET_TS(pCmd->tsComplete);
1498 VBOXDD_HGCMCALL_COMPLETED_REQ(pCmd, result);
1499 hgcmCompletedWorker(pInterface, result, pCmd);
1500#endif
1501}
1502
1503/**
1504 * @interface_method_impl{PDMIHGCMPORT,pfnIsCmdRestored}
1505 */
1506DECLCALLBACK(bool) hgcmIsCmdRestored(PPDMIHGCMPORT pInterface, PVBOXHGCMCMD pCmd)
1507{
1508 RT_NOREF(pInterface);
1509 return pCmd && pCmd->fRestored;
1510}
1511
1512/** Save information about pending HGCM requests from pThis->listHGCMCmd.
1513 *
1514 * @returns VBox status code that the guest should see.
1515 * @param pThis The VMMDev instance data.
1516 * @param pSSM SSM handle for SSM functions.
1517 *
1518 * @thread EMT
1519 */
1520int vmmdevHGCMSaveState(PVMMDEV pThis, PSSMHANDLE pSSM)
1521{
1522 LogFlowFunc(("\n"));
1523
1524 /* Compute how many commands are pending. */
1525 uint32_t cCmds = 0;
1526 PVBOXHGCMCMD pCmd;
1527 RTListForEach(&pThis->listHGCMCmd, pCmd, VBOXHGCMCMD, node)
1528 {
1529 LogFlowFunc(("pCmd %p\n", pCmd));
1530 ++cCmds;
1531 }
1532 LogFlowFunc(("cCmds = %d\n", cCmds));
1533
1534 /* Save number of commands. */
1535 int rc = SSMR3PutU32(pSSM, cCmds);
1536 AssertRCReturn(rc, rc);
1537
1538 if (cCmds > 0)
1539 {
1540 RTListForEach(&pThis->listHGCMCmd, pCmd, VBOXHGCMCMD, node)
1541 {
1542 LogFlowFunc(("Saving %RGp, size %d\n", pCmd->GCPhys, pCmd->cbRequest));
1543
1544 SSMR3PutU32 (pSSM, (uint32_t)pCmd->enmCmdType);
1545 SSMR3PutBool (pSSM, pCmd->fCancelled);
1546 SSMR3PutGCPhys (pSSM, pCmd->GCPhys);
1547 SSMR3PutU32 (pSSM, pCmd->cbRequest);
1548 SSMR3PutU32 (pSSM, (uint32_t)pCmd->enmRequestType);
1549 const uint32_t cParms = pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL ? pCmd->u.call.cParms : 0;
1550 rc = SSMR3PutU32(pSSM, cParms);
1551 AssertRCReturn(rc, rc);
1552
1553 if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL)
1554 {
1555 SSMR3PutU32 (pSSM, pCmd->u.call.u32ClientID);
1556 rc = SSMR3PutU32(pSSM, pCmd->u.call.u32Function);
1557 AssertRCReturn(rc, rc);
1558
1559 /* Guest parameters. */
1560 uint32_t i;
1561 for (i = 0; i < pCmd->u.call.cParms; ++i)
1562 {
1563 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
1564
1565 rc = SSMR3PutU32(pSSM, (uint32_t)pGuestParm->enmType);
1566 AssertRCReturn(rc, rc);
1567
1568 if ( pGuestParm->enmType == VMMDevHGCMParmType_32bit
1569 || pGuestParm->enmType == VMMDevHGCMParmType_64bit)
1570 {
1571 const VBOXHGCMPARMVAL * const pVal = &pGuestParm->u.val;
1572 SSMR3PutU64 (pSSM, pVal->u64Value);
1573 SSMR3PutU32 (pSSM, pVal->offValue);
1574 rc = SSMR3PutU32(pSSM, pVal->cbValue);
1575 }
1576 else if ( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In
1577 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out
1578 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr
1579 || pGuestParm->enmType == VMMDevHGCMParmType_PageList
1580 || pGuestParm->enmType == VMMDevHGCMParmType_Embedded)
1581 {
1582 const VBOXHGCMPARMPTR * const pPtr = &pGuestParm->u.ptr;
1583 SSMR3PutU32 (pSSM, pPtr->cbData);
1584 SSMR3PutU32 (pSSM, pPtr->offFirstPage);
1585 SSMR3PutU32 (pSSM, pPtr->cPages);
1586 rc = SSMR3PutU32(pSSM, pPtr->fu32Direction);
1587
1588 uint32_t iPage;
1589 for (iPage = 0; iPage < pPtr->cPages; ++iPage)
1590 rc = SSMR3PutGCPhys(pSSM, pPtr->paPages[iPage]);
1591 }
1592 else
1593 {
1594 AssertFailedStmt(rc = VERR_INTERNAL_ERROR);
1595 }
1596 AssertRCReturn(rc, rc);
1597 }
1598 }
1599 else if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
1600 {
1601 SSMR3PutU32(pSSM, pCmd->u.connect.u32ClientID);
1602 SSMR3PutMem(pSSM, pCmd->u.connect.pLoc, sizeof(*pCmd->u.connect.pLoc));
1603 }
1604 else if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT)
1605 {
1606 SSMR3PutU32(pSSM, pCmd->u.disconnect.u32ClientID);
1607 }
1608 else
1609 {
1610 AssertFailedReturn(VERR_INTERNAL_ERROR);
1611 }
1612
1613 /* A reserved field, will allow to extend saved data for a command. */
1614 rc = SSMR3PutU32(pSSM, 0);
1615 AssertRCReturn(rc, rc);
1616 }
1617 }
1618
1619 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
1620 rc = SSMR3PutU32(pSSM, 0);
1621 AssertRCReturn(rc, rc);
1622
1623 return rc;
1624}
1625
1626/** Load information about pending HGCM requests.
1627 *
1628 * Allocate VBOXHGCMCMD commands and add them to pThis->listHGCMCmd temporarily.
1629 * vmmdevHGCMLoadStateDone will process the temporary list.
1630 *
1631 * @returns VBox status code that the guest should see.
1632 * @param pThis The VMMDev instance data.
1633 * @param pSSM SSM handle for SSM functions.
1634 * @param uVersion Saved state version.
1635 *
1636 * @thread EMT
1637 */
1638int vmmdevHGCMLoadState(PVMMDEV pThis, PSSMHANDLE pSSM, uint32_t uVersion)
1639{
1640 LogFlowFunc(("\n"));
1641
1642 pThis->u32SSMVersion = uVersion; /* For vmmdevHGCMLoadStateDone */
1643
1644 /* Read how many commands were pending. */
1645 uint32_t cCmds = 0;
1646 int rc = SSMR3GetU32(pSSM, &cCmds);
1647 AssertRCReturn(rc, rc);
1648
1649 LogFlowFunc(("cCmds = %d\n", cCmds));
1650
1651 if (uVersion >= VMMDEV_SAVED_STATE_VERSION_HGCM_PARAMS)
1652 {
1653 /* Saved information about all HGCM parameters. */
1654 uint32_t u32;
1655
1656 uint32_t iCmd;
1657 for (iCmd = 0; iCmd < cCmds; ++iCmd)
1658 {
1659 /* Command fields. */
1660 VBOXHGCMCMDTYPE enmCmdType;
1661 bool fCancelled;
1662 RTGCPHYS GCPhys;
1663 uint32_t cbRequest;
1664 VMMDevRequestType enmRequestType;
1665 uint32_t cParms;
1666
1667 SSMR3GetU32 (pSSM, &u32);
1668 enmCmdType = (VBOXHGCMCMDTYPE)u32;
1669 SSMR3GetBool (pSSM, &fCancelled);
1670 SSMR3GetGCPhys (pSSM, &GCPhys);
1671 SSMR3GetU32 (pSSM, &cbRequest);
1672 SSMR3GetU32 (pSSM, &u32);
1673 enmRequestType = (VMMDevRequestType)u32;
1674 rc = SSMR3GetU32(pSSM, &cParms);
1675 AssertRCReturn(rc, rc);
1676
1677 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, enmCmdType, GCPhys, cbRequest, cParms);
1678 AssertReturn(pCmd, VERR_NO_MEMORY);
1679
1680 pCmd->fCancelled = fCancelled;
1681 pCmd->GCPhys = GCPhys;
1682 pCmd->cbRequest = cbRequest;
1683 pCmd->enmRequestType = enmRequestType;
1684
1685 if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
1686 {
1687 SSMR3GetU32 (pSSM, &pCmd->u.call.u32ClientID);
1688 rc = SSMR3GetU32(pSSM, &pCmd->u.call.u32Function);
1689 AssertRCReturn(rc, rc);
1690
1691 /* Guest parameters. */
1692 uint32_t i;
1693 for (i = 0; i < cParms; ++i)
1694 {
1695 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
1696
1697 rc = SSMR3GetU32(pSSM, &u32);
1698 AssertRCReturn(rc, rc);
1699 pGuestParm->enmType = (HGCMFunctionParameterType)u32;
1700
1701 if ( pGuestParm->enmType == VMMDevHGCMParmType_32bit
1702 || pGuestParm->enmType == VMMDevHGCMParmType_64bit)
1703 {
1704 VBOXHGCMPARMVAL * const pVal = &pGuestParm->u.val;
1705 SSMR3GetU64 (pSSM, &pVal->u64Value);
1706 SSMR3GetU32 (pSSM, &pVal->offValue);
1707 rc = SSMR3GetU32(pSSM, &pVal->cbValue);
1708 }
1709 else if ( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In
1710 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out
1711 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr
1712 || pGuestParm->enmType == VMMDevHGCMParmType_PageList
1713 || pGuestParm->enmType == VMMDevHGCMParmType_Embedded)
1714 {
1715 VBOXHGCMPARMPTR * const pPtr = &pGuestParm->u.ptr;
1716 SSMR3GetU32 (pSSM, &pPtr->cbData);
1717 SSMR3GetU32 (pSSM, &pPtr->offFirstPage);
1718 SSMR3GetU32 (pSSM, &pPtr->cPages);
1719 rc = SSMR3GetU32(pSSM, &pPtr->fu32Direction);
1720 if (RT_SUCCESS(rc))
1721 {
1722 if (pPtr->cPages == 1)
1723 pPtr->paPages = &pPtr->GCPhysSinglePage;
1724 else
1725 {
1726 AssertReturn(pGuestParm->enmType != VMMDevHGCMParmType_Embedded, VERR_INTERNAL_ERROR_3);
1727 pPtr->paPages = (RTGCPHYS *)RTMemAlloc(pPtr->cPages * sizeof(RTGCPHYS));
1728 AssertStmt(pPtr->paPages, rc = VERR_NO_MEMORY);
1729 }
1730
1731 if (RT_SUCCESS(rc))
1732 {
1733 uint32_t iPage;
1734 for (iPage = 0; iPage < pPtr->cPages; ++iPage)
1735 rc = SSMR3GetGCPhys(pSSM, &pPtr->paPages[iPage]);
1736 }
1737 }
1738 }
1739 else
1740 {
1741 AssertFailedStmt(rc = VERR_INTERNAL_ERROR);
1742 }
1743 AssertRCReturn(rc, rc);
1744 }
1745 }
1746 else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
1747 {
1748 SSMR3GetU32(pSSM, &pCmd->u.connect.u32ClientID);
1749 rc = SSMR3GetMem(pSSM, pCmd->u.connect.pLoc, sizeof(*pCmd->u.connect.pLoc));
1750 AssertRCReturn(rc, rc);
1751 }
1752 else if (enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT)
1753 {
1754 rc = SSMR3GetU32(pSSM, &pCmd->u.disconnect.u32ClientID);
1755 AssertRCReturn(rc, rc);
1756 }
1757 else
1758 {
1759 AssertFailedReturn(VERR_INTERNAL_ERROR);
1760 }
1761
1762 /* A reserved field, will allow to extend saved data for a command. */
1763 rc = SSMR3GetU32(pSSM, &u32);
1764 AssertRCReturn(rc, rc);
1765
1766 vmmdevHGCMAddCommand(pThis, pCmd);
1767 }
1768
1769 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
1770 rc = SSMR3GetU32(pSSM, &u32);
1771 AssertRCReturn(rc, rc);
1772 }
1773 else if (uVersion >= 9)
1774 {
1775 /* Version 9+: Load information about commands. Pre-rewrite. */
1776 uint32_t u32;
1777
1778 uint32_t iCmd;
1779 for (iCmd = 0; iCmd < cCmds; ++iCmd)
1780 {
1781 VBOXHGCMCMDTYPE enmCmdType;
1782 bool fCancelled;
1783 RTGCPHYS GCPhys;
1784 uint32_t cbRequest;
1785 uint32_t cLinAddrs;
1786
1787 SSMR3GetGCPhys (pSSM, &GCPhys);
1788 rc = SSMR3GetU32(pSSM, &cbRequest);
1789 AssertRCReturn(rc, rc);
1790
1791 LogFlowFunc(("Restoring %RGp size %x bytes\n", GCPhys, cbRequest));
1792
1793 /* For uVersion <= 12, this was the size of entire command.
1794 * Now the command is reconstructed in vmmdevHGCMLoadStateDone.
1795 */
1796 if (uVersion <= 12)
1797 SSMR3Skip(pSSM, sizeof (uint32_t));
1798
1799 SSMR3GetU32 (pSSM, &u32);
1800 enmCmdType = (VBOXHGCMCMDTYPE)u32;
1801 SSMR3GetBool (pSSM, &fCancelled);
1802 /* How many linear pointers. Always 0 if not a call command. */
1803 rc = SSMR3GetU32(pSSM, &cLinAddrs);
1804 AssertRCReturn(rc, rc);
1805
1806 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, enmCmdType, GCPhys, cbRequest, cLinAddrs);
1807 AssertReturn(pCmd, VERR_NO_MEMORY);
1808
1809 pCmd->fCancelled = fCancelled;
1810 pCmd->GCPhys = GCPhys;
1811 pCmd->cbRequest = cbRequest;
1812
1813 if (cLinAddrs > 0)
1814 {
1815 /* Skip number of pages for all LinAddrs in this command. */
1816 SSMR3Skip(pSSM, sizeof(uint32_t));
1817
1818 uint32_t i;
1819 for (i = 0; i < cLinAddrs; ++i)
1820 {
1821 VBOXHGCMPARMPTR * const pPtr = &pCmd->u.call.paGuestParms[i].u.ptr;
1822
1823 /* Index of the parameter. Use cbData field to store the index. */
1824 SSMR3GetU32 (pSSM, &pPtr->cbData);
1825 SSMR3GetU32 (pSSM, &pPtr->offFirstPage);
1826 rc = SSMR3GetU32(pSSM, &pPtr->cPages);
1827 AssertRCReturn(rc, rc);
1828
1829 pPtr->paPages = (RTGCPHYS *)RTMemAlloc(pPtr->cPages * sizeof(RTGCPHYS));
1830 AssertReturn(pPtr->paPages, VERR_NO_MEMORY);
1831
1832 uint32_t iPage;
1833 for (iPage = 0; iPage < pPtr->cPages; ++iPage)
1834 rc = SSMR3GetGCPhys(pSSM, &pPtr->paPages[iPage]);
1835 }
1836 }
1837
1838 /* A reserved field, will allow to extend saved data for a command. */
1839 rc = SSMR3GetU32(pSSM, &u32);
1840 AssertRCReturn(rc, rc);
1841
1842 vmmdevHGCMAddCommand(pThis, pCmd);
1843 }
1844
1845 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
1846 rc = SSMR3GetU32(pSSM, &u32);
1847 AssertRCReturn(rc, rc);
1848 }
1849 else
1850 {
1851 /* Ancient. Only the guest physical address is saved. */
1852 uint32_t iCmd;
1853 for (iCmd = 0; iCmd < cCmds; ++iCmd)
1854 {
1855 RTGCPHYS GCPhys;
1856 uint32_t cbRequest;
1857
1858 SSMR3GetGCPhys(pSSM, &GCPhys);
1859 rc = SSMR3GetU32(pSSM, &cbRequest);
1860 AssertRCReturn(rc, rc);
1861
1862 LogFlowFunc(("Restoring %RGp size %x bytes\n", GCPhys, cbRequest));
1863
1864 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_LOADSTATE, GCPhys, cbRequest, 0);
1865 AssertReturn(pCmd, VERR_NO_MEMORY);
1866
1867 vmmdevHGCMAddCommand(pThis, pCmd);
1868 }
1869 }
1870
1871 return rc;
1872}
1873
1874/** Restore HGCM connect command loaded from old saved state.
1875 *
1876 * @returns VBox status code that the guest should see.
1877 * @param pThis The VMMDev instance data.
1878 * @param u32SSMVersion The saved state version the command has been loaded from.
1879 * @param pLoadedCmd Command loaded from saved state, it is imcomplete and needs restoration.
1880 * @param pReq The guest request (cached in host memory).
1881 * @param cbReq Size of the guest request.
1882 * @param enmRequestType Type of the HGCM request.
1883 * @param ppRestoredCmd Where to store pointer to newly allocated restored command.
1884 */
1885static int vmmdevHGCMRestoreConnect(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
1886 VMMDevHGCMConnect *pReq, uint32_t cbReq, VMMDevRequestType enmRequestType,
1887 VBOXHGCMCMD **ppRestoredCmd)
1888{
1889 RT_NOREF(pThis);
1890
1891 int rc = VINF_SUCCESS;
1892
1893 /* Verify the request. */
1894 ASSERT_GUEST_RETURN(cbReq >= sizeof(*pReq), VERR_MISMATCH);
1895 if (u32SSMVersion >= 9)
1896 {
1897 ASSERT_GUEST_RETURN(pLoadedCmd->enmCmdType == VBOXHGCMCMDTYPE_CONNECT, VERR_MISMATCH);
1898 }
1899
1900 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_CONNECT, pLoadedCmd->GCPhys, cbReq, 0);
1901 AssertReturn(pCmd, VERR_NO_MEMORY);
1902
1903 if (u32SSMVersion >= 9)
1904 pCmd->fCancelled = pLoadedCmd->fCancelled;
1905 else
1906 pCmd->fCancelled = false;
1907 pCmd->fRestored = true;
1908 pCmd->enmRequestType = enmRequestType;
1909
1910 vmmdevHGCMConnectFetch(pReq, pCmd);
1911
1912 if (RT_SUCCESS(rc))
1913 *ppRestoredCmd = pCmd;
1914
1915 return rc;
1916}
1917
1918/** Restore HGCM disconnect command loaded from old saved state.
1919 *
1920 * @returns VBox status code that the guest should see.
1921 * @param pThis The VMMDev instance data.
1922 * @param u32SSMVersion The saved state version the command has been loaded from.
1923 * @param pLoadedCmd Command loaded from saved state, it is imcomplete and needs restoration.
1924 * @param pReq The guest request (cached in host memory).
1925 * @param cbReq Size of the guest request.
1926 * @param enmRequestType Type of the HGCM request.
1927 * @param ppRestoredCmd Where to store pointer to newly allocated restored command.
1928 */
1929static int vmmdevHGCMRestoreDisconnect(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
1930 VMMDevHGCMDisconnect *pReq, uint32_t cbReq, VMMDevRequestType enmRequestType,
1931 VBOXHGCMCMD **ppRestoredCmd)
1932{
1933 RT_NOREF(pThis);
1934
1935 int rc = VINF_SUCCESS;
1936
1937 /* Verify the request. */
1938 ASSERT_GUEST_RETURN(cbReq >= sizeof(*pReq), VERR_MISMATCH);
1939 if (u32SSMVersion >= 9)
1940 {
1941 ASSERT_GUEST_RETURN(pLoadedCmd->enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT, VERR_MISMATCH);
1942 }
1943
1944 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_DISCONNECT, pLoadedCmd->GCPhys, cbReq, 0);
1945 AssertReturn(pCmd, VERR_NO_MEMORY);
1946
1947 if (u32SSMVersion >= 9)
1948 pCmd->fCancelled = pLoadedCmd->fCancelled;
1949 else
1950 pCmd->fCancelled = false;
1951 pCmd->fRestored = true;
1952 pCmd->enmRequestType = enmRequestType;
1953
1954 vmmdevHGCMDisconnectFetch(pReq, pCmd);
1955
1956 if (RT_SUCCESS(rc))
1957 *ppRestoredCmd = pCmd;
1958
1959 return rc;
1960}
1961
1962/** Restore HGCM call command loaded from old saved state.
1963 *
1964 * @returns VBox status code that the guest should see.
1965 * @param pThis The VMMDev instance data.
1966 * @param u32SSMVersion The saved state version the command has been loaded from.
1967 * @param pLoadedCmd Command loaded from saved state, it is imcomplete and needs restoration.
1968 * @param pReq The guest request (cached in host memory).
1969 * @param cbReq Size of the guest request.
1970 * @param enmRequestType Type of the HGCM request.
1971 * @param ppRestoredCmd Where to store pointer to newly allocated restored command.
1972 */
1973static int vmmdevHGCMRestoreCall(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
1974 VMMDevHGCMCall *pReq, uint32_t cbReq, VMMDevRequestType enmRequestType,
1975 VBOXHGCMCMD **ppRestoredCmd)
1976{
1977 int rc = VINF_SUCCESS;
1978
1979 /* Verify the request. */
1980 ASSERT_GUEST_RETURN(cbReq >= sizeof(*pReq), VERR_MISMATCH);
1981 if (u32SSMVersion >= 9)
1982 {
1983 ASSERT_GUEST_RETURN(pLoadedCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL, VERR_MISMATCH);
1984 }
1985
1986 PVBOXHGCMCMD pCmd;
1987 uint32_t cbHGCMParmStruct;
1988 rc = vmmdevHGCMCallAlloc(pThis, pReq, cbReq, pLoadedCmd->GCPhys, enmRequestType, &pCmd, &cbHGCMParmStruct);
1989 if (RT_FAILURE(rc))
1990 return rc;
1991
1992 /* pLoadedCmd is fake, it does not contain actual call parameters. Only pagelists for LinAddr. */
1993 if (u32SSMVersion >= 9)
1994 pCmd->fCancelled = pLoadedCmd->fCancelled;
1995 else
1996 pCmd->fCancelled = false;
1997 pCmd->fRestored = true;
1998 pCmd->enmRequestType = enmRequestType;
1999
2000 rc = vmmdevHGCMCallFetchGuestParms(pThis, pCmd, pReq, cbReq, enmRequestType, cbHGCMParmStruct);
2001 if (RT_SUCCESS(rc))
2002 {
2003 /* Update LinAddr parameters from pLoadedCmd.
2004 * pLoadedCmd->u.call.cParms is actually the number of LinAddrs, see vmmdevHGCMLoadState.
2005 */
2006 uint32_t iLinAddr;
2007 for (iLinAddr = 0; iLinAddr < pLoadedCmd->u.call.cParms; ++iLinAddr)
2008 {
2009 VBOXHGCMGUESTPARM * const pLoadedParm = &pLoadedCmd->u.call.paGuestParms[iLinAddr];
2010 /* pLoadedParm->cbData is actually index of the LinAddr parameter, see vmmdevHGCMLoadState. */
2011 const uint32_t iParm = pLoadedParm->u.ptr.cbData;
2012 ASSERT_GUEST_STMT_BREAK(iParm < pCmd->u.call.cParms, rc = VERR_MISMATCH);
2013
2014 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[iParm];
2015 ASSERT_GUEST_STMT_BREAK( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In
2016 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out
2017 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr,
2018 rc = VERR_MISMATCH);
2019 ASSERT_GUEST_STMT_BREAK( pLoadedParm->u.ptr.offFirstPage == pGuestParm->u.ptr.offFirstPage
2020 && pLoadedParm->u.ptr.cPages == pGuestParm->u.ptr.cPages,
2021 rc = VERR_MISMATCH);
2022 memcpy(pGuestParm->u.ptr.paPages, pLoadedParm->u.ptr.paPages, pGuestParm->u.ptr.cPages * sizeof(RTGCPHYS));
2023 }
2024 }
2025
2026 if (RT_SUCCESS(rc))
2027 *ppRestoredCmd = pCmd;
2028 else
2029 vmmdevHGCMCmdFree(pThis, pCmd);
2030
2031 return rc;
2032}
2033
2034/** Allocate and initialize a HGCM command using the given request (pReqHdr)
2035 * and command loaded from saved state (pCmd).
2036 *
2037 * @returns VBox status code that the guest should see.
2038 * @param pThis The VMMDev instance data.
2039 * @param u32SSMVersion Saved state version.
2040 * @param pLoadedCmd HGCM command which needs restoration.
2041 * @param pReqHdr The request (cached in host memory).
2042 * @param cbReq Size of the entire request (including HGCM parameters).
2043 * @param ppRestoredCmd Where to store pointer to restored command.
2044 */
2045static int vmmdevHGCMRestoreCommand(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
2046 const VMMDevHGCMRequestHeader *pReqHdr, uint32_t cbReq,
2047 VBOXHGCMCMD **ppRestoredCmd)
2048{
2049 int rc = VINF_SUCCESS;
2050
2051 /* Verify the request. */
2052 ASSERT_GUEST_RETURN(cbReq >= sizeof(VMMDevHGCMRequestHeader), VERR_MISMATCH);
2053 ASSERT_GUEST_RETURN(cbReq == pReqHdr->header.size, VERR_MISMATCH);
2054
2055 const VMMDevRequestType enmRequestType = pReqHdr->header.requestType;
2056 switch (enmRequestType)
2057 {
2058 case VMMDevReq_HGCMConnect:
2059 {
2060 VMMDevHGCMConnect *pReq = (VMMDevHGCMConnect *)pReqHdr;
2061 rc = vmmdevHGCMRestoreConnect(pThis, u32SSMVersion, pLoadedCmd, pReq, cbReq, enmRequestType,
2062 ppRestoredCmd);
2063 break;
2064 }
2065
2066 case VMMDevReq_HGCMDisconnect:
2067 {
2068 VMMDevHGCMDisconnect *pReq = (VMMDevHGCMDisconnect *)pReqHdr;
2069 rc = vmmdevHGCMRestoreDisconnect(pThis, u32SSMVersion, pLoadedCmd, pReq, cbReq, enmRequestType,
2070 ppRestoredCmd);
2071 break;
2072 }
2073
2074#ifdef VBOX_WITH_64_BITS_GUESTS
2075 case VMMDevReq_HGCMCall32:
2076 case VMMDevReq_HGCMCall64:
2077#else
2078 case VMMDevReq_HGCMCall:
2079#endif
2080 {
2081 VMMDevHGCMCall *pReq = (VMMDevHGCMCall *)pReqHdr;
2082 rc = vmmdevHGCMRestoreCall(pThis, u32SSMVersion, pLoadedCmd, pReq, cbReq, enmRequestType,
2083 ppRestoredCmd);
2084 break;
2085 }
2086
2087 default:
2088 ASSERT_GUEST_FAILED_RETURN(VERR_MISMATCH);
2089 }
2090
2091 return rc;
2092}
2093
2094/** Resubmit pending HGCM commands which were loaded form saved state.
2095 *
2096 * @returns VBox status code.
2097 * @param pThis The VMMDev instance data.
2098 *
2099 * @thread EMT
2100 */
2101int vmmdevHGCMLoadStateDone(PVMMDEV pThis)
2102{
2103 /*
2104 * Resubmit pending HGCM commands to services.
2105 *
2106 * pThis->pHGCMCmdList contains commands loaded by vmmdevHGCMLoadState.
2107 *
2108 * Legacy saved states (pre VMMDEV_SAVED_STATE_VERSION_HGCM_PARAMS)
2109 * do not have enough information about the command parameters,
2110 * therefore it is necessary to reload at least some data from the
2111 * guest memory to construct commands.
2112 *
2113 * There are two types of legacy saved states which contain:
2114 * 1) the guest physical address and size of request;
2115 * 2) additionally page lists for LinAddr parameters.
2116 *
2117 * Legacy commands have enmCmdType = VBOXHGCMCMDTYPE_LOADSTATE?
2118 */
2119
2120 int rcFunc = VINF_SUCCESS; /* This status code will make the function fail. I.e. VM will not start. */
2121
2122 /* Get local copy of the list of loaded commands. */
2123 RTLISTANCHOR listLoadedCommands;
2124 RTListMove(&listLoadedCommands, &pThis->listHGCMCmd);
2125
2126 /* Resubmit commands. */
2127 PVBOXHGCMCMD pCmd, pNext;
2128 RTListForEachSafe(&listLoadedCommands, pCmd, pNext, VBOXHGCMCMD, node)
2129 {
2130 int rcCmd = VINF_SUCCESS; /* This status code will make the HGCM command fail for the guest. */
2131
2132 RTListNodeRemove(&pCmd->node);
2133
2134 /*
2135 * Re-read the request from the guest memory.
2136 * It will be used to:
2137 * * reconstruct commands if legacy saved state has been restored;
2138 * * report an error to the guest if resubmit failed.
2139 */
2140 VMMDevHGCMRequestHeader *pReqHdr = (VMMDevHGCMRequestHeader *)RTMemAlloc(pCmd->cbRequest);
2141 AssertBreakStmt(pReqHdr, vmmdevHGCMCmdFree(pThis, pCmd); rcFunc = VERR_NO_MEMORY);
2142
2143 PDMDevHlpPhysRead(pThis->pDevIns, pCmd->GCPhys, pReqHdr, pCmd->cbRequest);
2144 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2145
2146 if (pThis->pHGCMDrv)
2147 {
2148 /*
2149 * Reconstruct legacy commands.
2150 */
2151 if (RT_LIKELY(pThis->u32SSMVersion >= VMMDEV_SAVED_STATE_VERSION_HGCM_PARAMS))
2152 { /* likely */ }
2153 else
2154 {
2155 PVBOXHGCMCMD pRestoredCmd = NULL;
2156 rcCmd = vmmdevHGCMRestoreCommand(pThis, pThis->u32SSMVersion, pCmd,
2157 pReqHdr, pCmd->cbRequest, &pRestoredCmd);
2158 if (RT_SUCCESS(rcCmd))
2159 {
2160 Assert(pCmd != pRestoredCmd); /* vmmdevHGCMRestoreCommand must allocate restored command. */
2161 vmmdevHGCMCmdFree(pThis, pCmd);
2162 pCmd = pRestoredCmd;
2163 }
2164 }
2165
2166 /* Resubmit commands. */
2167 if (RT_SUCCESS(rcCmd))
2168 {
2169 switch (pCmd->enmCmdType)
2170 {
2171 case VBOXHGCMCMDTYPE_CONNECT:
2172 {
2173 vmmdevHGCMAddCommand(pThis, pCmd);
2174 rcCmd = pThis->pHGCMDrv->pfnConnect(pThis->pHGCMDrv, pCmd, pCmd->u.connect.pLoc,
2175 &pCmd->u.connect.u32ClientID);
2176 if (RT_FAILURE(rcCmd))
2177 vmmdevHGCMRemoveCommand(pThis, pCmd);
2178 break;
2179 }
2180
2181 case VBOXHGCMCMDTYPE_DISCONNECT:
2182 {
2183 vmmdevHGCMAddCommand(pThis, pCmd);
2184 rcCmd = pThis->pHGCMDrv->pfnDisconnect(pThis->pHGCMDrv, pCmd, pCmd->u.disconnect.u32ClientID);
2185 if (RT_FAILURE(rcCmd))
2186 vmmdevHGCMRemoveCommand(pThis, pCmd);
2187 break;
2188 }
2189
2190 case VBOXHGCMCMDTYPE_CALL:
2191 {
2192 rcCmd = vmmdevHGCMInitHostParameters(pThis, pCmd, (uint8_t const *)pReqHdr);
2193 if (RT_SUCCESS(rcCmd))
2194 {
2195 vmmdevHGCMAddCommand(pThis, pCmd);
2196
2197 /* Pass the function call to HGCM connector for actual processing */
2198 uint64_t tsNow;
2199 STAM_GET_TS(tsNow);
2200 rcCmd = pThis->pHGCMDrv->pfnCall(pThis->pHGCMDrv, pCmd,
2201 pCmd->u.call.u32ClientID, pCmd->u.call.u32Function,
2202 pCmd->u.call.cParms, pCmd->u.call.paHostParms, tsNow);
2203 if (RT_FAILURE(rcCmd))
2204 {
2205 LogFunc(("pfnCall rc = %Rrc\n", rcCmd));
2206 vmmdevHGCMRemoveCommand(pThis, pCmd);
2207 }
2208 }
2209 break;
2210 }
2211
2212 default:
2213 AssertFailedStmt(rcCmd = VERR_INTERNAL_ERROR);
2214 }
2215 }
2216 }
2217 else
2218 AssertFailedStmt(rcCmd = VERR_INTERNAL_ERROR);
2219
2220 if (RT_SUCCESS(rcCmd))
2221 { /* likely */ }
2222 else
2223 {
2224 /* Return the error to the guest. Guest may try to repeat the call. */
2225 pReqHdr->result = rcCmd;
2226 pReqHdr->header.rc = rcCmd;
2227 pReqHdr->fu32Flags |= VBOX_HGCM_REQ_DONE;
2228
2229 /* Write back only the header. */
2230 PDMDevHlpPhysWrite(pThis->pDevIns, pCmd->GCPhys, pReqHdr, sizeof(*pReqHdr));
2231
2232 VMMDevNotifyGuest(pThis, VMMDEV_EVENT_HGCM);
2233
2234 /* Deallocate the command memory. */
2235 vmmdevHGCMCmdFree(pThis, pCmd);
2236 }
2237
2238 RTMemFree(pReqHdr);
2239 }
2240
2241 if (RT_FAILURE(rcFunc))
2242 {
2243 RTListForEachSafe(&listLoadedCommands, pCmd, pNext, VBOXHGCMCMD, node)
2244 {
2245 RTListNodeRemove(&pCmd->node);
2246 vmmdevHGCMCmdFree(pThis, pCmd);
2247 }
2248 }
2249
2250 return rcFunc;
2251}
2252
2253
2254/**
2255 * Counterpart to vmmdevHGCMInit().
2256 *
2257 * @param pThis The VMMDev instance data.
2258 */
2259void vmmdevHGCMDestroy(PVMMDEV pThis)
2260{
2261 LogFlowFunc(("\n"));
2262
2263 if (RTCritSectIsInitialized(&pThis->critsectHGCMCmdList))
2264 {
2265 PVBOXHGCMCMD pCmd, pNext;
2266 RTListForEachSafe(&pThis->listHGCMCmd, pCmd, pNext, VBOXHGCMCMD, node)
2267 {
2268 vmmdevHGCMRemoveCommand(pThis, pCmd);
2269 vmmdevHGCMCmdFree(pThis, pCmd);
2270 }
2271
2272 RTCritSectDelete(&pThis->critsectHGCMCmdList);
2273 }
2274
2275 AssertCompile((uintptr_t)NIL_RTMEMCACHE == 0);
2276 if (pThis->hHgcmCmdCache != NIL_RTMEMCACHE)
2277 {
2278 RTMemCacheDestroy(pThis->hHgcmCmdCache);
2279 pThis->hHgcmCmdCache = NIL_RTMEMCACHE;
2280 }
2281}
2282
2283
2284/**
2285 * Initializes the HGCM specific state.
2286 *
2287 * Keeps VBOXHGCMCMDCACHED and friends local.
2288 *
2289 * @returns VBox status code.
2290 * @param pThis The VMMDev instance data.
2291 */
2292int vmmdevHGCMInit(PVMMDEV pThis)
2293{
2294 LogFlowFunc(("\n"));
2295
2296 RTListInit(&pThis->listHGCMCmd);
2297
2298 int rc = RTCritSectInit(&pThis->critsectHGCMCmdList);
2299 AssertLogRelRCReturn(rc, rc);
2300
2301 rc = RTMemCacheCreate(&pThis->hHgcmCmdCache, sizeof(VBOXHGCMCMDCACHED), 64, _1M, NULL, NULL, NULL, 0);
2302 AssertLogRelRCReturn(rc, rc);
2303
2304 pThis->u32HGCMEnabled = 0;
2305
2306 return VINF_SUCCESS;
2307}
2308
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