VirtualBox

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

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

VMMDevHGCM: Fix handling of embedded buffers in the non-locked code path. bugref:9172

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette