VirtualBox

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

Last change on this file since 95506 was 93944, checked in by vboxsync, 3 years ago

Devices: Must not use PAGE_SIZE, PAGE_SHIFT, PAGE_OFFSET_MASK, PAGE_ADDRESS or PHYS_PAGE_ADDRESS here either. bugref:9898

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