VirtualBox

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

Last change on this file since 88037 was 83558, checked in by vboxsync, 5 years ago

VMMDev: Adjusted RTMemFreeZ use - must use our private pGuestParm->u.ptr.cbData member not the pHostParm->u.pointer.size one as it can have been modified by the HGCM service. bugref:9698

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