VirtualBox

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

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

VMMDev: Simplify VMMDevReq_HGCMCall,VMMDevReq_HGCMCall32, and VMMDevReq_HGCMCall64 definitions. bugref:9172

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