VirtualBox

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

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

VMMDev: Do proper heap usage accounting for HGCM requests. bugref:9379

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