VirtualBox

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

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

VMMDevHGCM: doxyfix bugref:9105

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