VirtualBox

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

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

Copyright year updates by scm.

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