VirtualBox

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

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

VMMDev/HGCM,VBoxGuest: New page list variant for more effectively passing physical contiguous buffers (VbglR0PhysHeapAlloc, kmalloc, ++). bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 92.9 KB
Line 
1/* $Id: VMMDevHGCM.cpp 76196 2018-12-12 19:24:05Z 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 || 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_HGCMCall, 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], pvSrc, cbToCopy);
1285 if (RT_FAILURE(rc))
1286 break;
1287 }
1288 break;
1289 }
1290
1291 default:
1292 break;
1293 }
1294 }
1295
1296 return VINF_SUCCESS;
1297}
1298
1299/** Update HGCM request in the guest memory and mark it as completed.
1300 *
1301 * @returns VINF_SUCCESS or VERR_CANCELLED.
1302 * @param pInterface Pointer to this PDM interface.
1303 * @param result HGCM completion status code (VBox status code).
1304 * @param pCmd Completed command, which contains updated host parameters.
1305 *
1306 * @thread EMT
1307 */
1308static int hgcmCompletedWorker(PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
1309{
1310 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1311#ifdef VBOX_WITH_DTRACE
1312 uint32_t idFunction = 0;
1313 uint32_t idClient = 0;
1314#endif
1315
1316 if (result == VINF_HGCM_SAVE_STATE)
1317 {
1318 /* If the completion routine was called while the HGCM service saves its state,
1319 * then currently nothing to be done here. The pCmd stays in the list and will
1320 * be saved later when the VMMDev state will be saved and re-submitted on load.
1321 *
1322 * It it assumed that VMMDev saves state after the HGCM services (VMMDev driver
1323 * attached by constructor before it registers its SSM state), and, therefore,
1324 * VBOXHGCMCMD structures are not removed by vmmdevHGCMSaveState from the list,
1325 * while HGCM uses them.
1326 */
1327 LogFlowFunc(("VINF_HGCM_SAVE_STATE for command %p\n", pCmd));
1328 return VINF_SUCCESS;
1329 }
1330
1331 VBOXDD_HGCMCALL_COMPLETED_EMT(pCmd, result);
1332
1333 int rc = VINF_SUCCESS;
1334
1335 /*
1336 * The cancellation protocol requires us to remove the command here
1337 * and then check the flag. Cancelled commands must not be written
1338 * back to guest memory.
1339 */
1340 vmmdevHGCMRemoveCommand(pThis, pCmd);
1341
1342 if (RT_LIKELY(!pCmd->fCancelled))
1343 {
1344 if (!pCmd->pvReqLocked)
1345 {
1346 /*
1347 * Request is not locked:
1348 */
1349 VMMDevHGCMRequestHeader *pHeader = (VMMDevHGCMRequestHeader *)RTMemAlloc(pCmd->cbRequest);
1350 if (pHeader)
1351 {
1352 /*
1353 * Read the request from the guest memory for updating.
1354 * The request data is not be used for anything but checking the request type.
1355 */
1356 PDMDevHlpPhysRead(pThis->pDevInsR3, pCmd->GCPhys, pHeader, pCmd->cbRequest);
1357 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
1358
1359 /* Verify the request type. This is the only field which is used from the guest memory. */
1360 const VMMDevRequestType enmRequestType = pHeader->header.requestType;
1361 if ( enmRequestType == pCmd->enmRequestType
1362 || enmRequestType == VMMDevReq_HGCMCancel)
1363 {
1364 RT_UNTRUSTED_VALIDATED_FENCE();
1365
1366 /*
1367 * Update parameters and data buffers.
1368 */
1369 switch (enmRequestType)
1370 {
1371#ifdef VBOX_WITH_64_BITS_GUESTS
1372 case VMMDevReq_HGCMCall64:
1373 case VMMDevReq_HGCMCall32:
1374#else
1375 case VMMDevReq_HGCMCall:
1376#endif
1377 {
1378 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
1379 rc = vmmdevHGCMCompleteCallRequest(pThis, pCmd, pHGCMCall, (uint8_t *)pHeader);
1380#ifdef VBOX_WITH_DTRACE
1381 idFunction = pCmd->u.call.u32Function;
1382 idClient = pCmd->u.call.u32ClientID;
1383#endif
1384 break;
1385 }
1386
1387 case VMMDevReq_HGCMConnect:
1388 {
1389 /* save the client id in the guest request packet */
1390 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)pHeader;
1391 pHGCMConnect->u32ClientID = pCmd->u.connect.u32ClientID;
1392 break;
1393 }
1394
1395 default:
1396 /* make compiler happy */
1397 break;
1398 }
1399 }
1400 else
1401 {
1402 /* Guest has changed the command type. */
1403 LogRelMax(50, ("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n",
1404 pCmd->enmCmdType, pHeader->header.requestType));
1405
1406 ASSERT_GUEST_FAILED_STMT(rc = VERR_INVALID_PARAMETER);
1407 }
1408
1409 /* Setup return code for the guest. */
1410 if (RT_SUCCESS(rc))
1411 pHeader->result = result;
1412 else
1413 pHeader->result = rc;
1414
1415 /* First write back the request. */
1416 PDMDevHlpPhysWrite(pThis->pDevInsR3, pCmd->GCPhys, pHeader, pCmd->cbRequest);
1417
1418 /* Mark request as processed. */
1419 pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
1420
1421 /* Second write the flags to mark the request as processed. */
1422 PDMDevHlpPhysWrite(pThis->pDevInsR3, pCmd->GCPhys + RT_UOFFSETOF(VMMDevHGCMRequestHeader, fu32Flags),
1423 &pHeader->fu32Flags, sizeof(pHeader->fu32Flags));
1424
1425 /* Now, when the command was removed from the internal list, notify the guest. */
1426 VMMDevNotifyGuest(pThis, VMMDEV_EVENT_HGCM);
1427
1428 RTMemFree(pHeader);
1429 }
1430 else
1431 {
1432 LogRelMax(10, ("VMMDev: Failed to allocate %u bytes for HGCM request completion!!!\n", pCmd->cbRequest));
1433 }
1434 }
1435 /*
1436 * Request was locked:
1437 */
1438 else
1439 {
1440 VMMDevHGCMRequestHeader volatile *pHeader = (VMMDevHGCMRequestHeader volatile *)pCmd->pvReqLocked;
1441
1442 /* Verify the request type. This is the only field which is used from the guest memory. */
1443 const VMMDevRequestType enmRequestType = pHeader->header.requestType;
1444 if ( enmRequestType == pCmd->enmRequestType
1445 || enmRequestType == VMMDevReq_HGCMCancel)
1446 {
1447 RT_UNTRUSTED_VALIDATED_FENCE();
1448
1449 /*
1450 * Update parameters and data buffers.
1451 */
1452 switch (enmRequestType)
1453 {
1454#ifdef VBOX_WITH_64_BITS_GUESTS
1455 case VMMDevReq_HGCMCall64:
1456 case VMMDevReq_HGCMCall32:
1457#else
1458 case VMMDevReq_HGCMCall:
1459#endif
1460 {
1461 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
1462 rc = vmmdevHGCMCompleteCallRequest(pThis, pCmd, pHGCMCall, (uint8_t *)pHeader);
1463#ifdef VBOX_WITH_DTRACE
1464 idFunction = pCmd->u.call.u32Function;
1465 idClient = pCmd->u.call.u32ClientID;
1466#endif
1467 break;
1468 }
1469
1470 case VMMDevReq_HGCMConnect:
1471 {
1472 /* save the client id in the guest request packet */
1473 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)pHeader;
1474 pHGCMConnect->u32ClientID = pCmd->u.connect.u32ClientID;
1475 break;
1476 }
1477
1478 default:
1479 /* make compiler happy */
1480 break;
1481 }
1482 }
1483 else
1484 {
1485 /* Guest has changed the command type. */
1486 LogRelMax(50, ("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n",
1487 pCmd->enmCmdType, pHeader->header.requestType));
1488
1489 ASSERT_GUEST_FAILED_STMT(rc = VERR_INVALID_PARAMETER);
1490 }
1491
1492 /* Setup return code for the guest. */
1493 if (RT_SUCCESS(rc))
1494 pHeader->result = result;
1495 else
1496 pHeader->result = rc;
1497
1498 /* Mark request as processed. */
1499 ASMAtomicOrU32(&pHeader->fu32Flags, VBOX_HGCM_REQ_DONE);
1500
1501 /* Now, when the command was removed from the internal list, notify the guest. */
1502 VMMDevNotifyGuest(pThis, VMMDEV_EVENT_HGCM);
1503 }
1504
1505 /* Set the status to success for now, though we might consider passing
1506 along the vmmdevHGCMCompleteCallRequest errors... */
1507 rc = VINF_SUCCESS;
1508 }
1509 else
1510 {
1511 LogFlowFunc(("Cancelled command %p\n", pCmd));
1512 rc = VERR_CANCELLED;
1513 }
1514
1515#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
1516 /* Save for final stats. */
1517 uint64_t const tsArrival = pCmd->tsArrival;
1518 uint64_t const tsComplete = pCmd->tsComplete;
1519#endif
1520
1521 /* Deallocate the command memory. */
1522 VBOXDD_HGCMCALL_COMPLETED_DONE(pCmd, idFunction, idClient, result);
1523 vmmdevHGCMCmdFree(pThis, pCmd);
1524
1525#ifndef VBOX_WITHOUT_RELEASE_STATISTICS
1526 /* Update stats. */
1527 uint64_t tsNow;
1528 STAM_GET_TS(tsNow);
1529 STAM_REL_PROFILE_ADD_PERIOD(&pThis->StatHgcmCmdCompletion, tsNow - tsComplete);
1530 if (tsArrival != 0)
1531 STAM_REL_PROFILE_ADD_PERIOD(&pThis->StatHgcmCmdTotal, tsNow - tsArrival);
1532#endif
1533
1534 return rc;
1535}
1536
1537/**
1538 * HGCM callback for request completion. Forwards to hgcmCompletedWorker.
1539 *
1540 * @returns VINF_SUCCESS or VERR_CANCELLED.
1541 * @param pInterface Pointer to this PDM interface.
1542 * @param result HGCM completion status code (VBox status code).
1543 * @param pCmd Completed command, which contains updated host parameters.
1544 */
1545DECLCALLBACK(int) hgcmCompleted(PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
1546{
1547#if 0 /* This seems to be significantly slower. Half of MsgTotal time seems to be spend here. */
1548 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1549 STAM_GET_TS(pCmd->tsComplete);
1550
1551 VBOXDD_HGCMCALL_COMPLETED_REQ(pCmd, result);
1552
1553/** @todo no longer necessary to forward to EMT, but it might be more
1554 * efficient...? */
1555 /* Not safe to execute asynchronously; forward to EMT */
1556 int rc = VMR3ReqCallVoidNoWait(PDMDevHlpGetVM(pThis->pDevInsR3), VMCPUID_ANY,
1557 (PFNRT)hgcmCompletedWorker, 3, pInterface, result, pCmd);
1558 AssertRC(rc);
1559 return VINF_SUCCESS; /* cannot tell if canceled or not... */
1560#else
1561 STAM_GET_TS(pCmd->tsComplete);
1562 VBOXDD_HGCMCALL_COMPLETED_REQ(pCmd, result);
1563 return hgcmCompletedWorker(pInterface, result, pCmd);
1564#endif
1565}
1566
1567/**
1568 * @interface_method_impl{PDMIHGCMPORT,pfnIsCmdRestored}
1569 */
1570DECLCALLBACK(bool) hgcmIsCmdRestored(PPDMIHGCMPORT pInterface, PVBOXHGCMCMD pCmd)
1571{
1572 RT_NOREF(pInterface);
1573 return pCmd && pCmd->fRestored;
1574}
1575
1576/**
1577 * @interface_method_impl{PDMIHGCMPORT,pfnIsCmdCancelled}
1578 */
1579DECLCALLBACK(bool) hgcmIsCmdCancelled(PPDMIHGCMPORT pInterface, PVBOXHGCMCMD pCmd)
1580{
1581 RT_NOREF(pInterface);
1582 return pCmd && pCmd->fCancelled;
1583}
1584
1585/**
1586 * @interface_method_impl{PDMIHGCMPORT,pfnGetRequestor}
1587 */
1588DECLCALLBACK(uint32_t) hgcmGetRequestor(PPDMIHGCMPORT pInterface, PVBOXHGCMCMD pCmd)
1589{
1590 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1591 AssertPtrReturn(pCmd, VMMDEV_REQUESTOR_LOWEST);
1592 if (pThis->guestInfo2.fFeatures & VBOXGSTINFO2_F_REQUESTOR_INFO)
1593 return pCmd->fRequestor;
1594 return VMMDEV_REQUESTOR_LEGACY;
1595}
1596
1597/**
1598 * @interface_method_impl{PDMIHGCMPORT,pfnGetVMMDevSessionId}
1599 */
1600DECLCALLBACK(uint64_t) hgcmGetVMMDevSessionId(PPDMIHGCMPORT pInterface)
1601{
1602 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1603 return pThis->idSession;
1604}
1605
1606/** Save information about pending HGCM requests from pThis->listHGCMCmd.
1607 *
1608 * @returns VBox status code that the guest should see.
1609 * @param pThis The VMMDev instance data.
1610 * @param pSSM SSM handle for SSM functions.
1611 *
1612 * @thread EMT
1613 */
1614int vmmdevHGCMSaveState(PVMMDEV pThis, PSSMHANDLE pSSM)
1615{
1616 LogFlowFunc(("\n"));
1617
1618 /* Compute how many commands are pending. */
1619 uint32_t cCmds = 0;
1620 PVBOXHGCMCMD pCmd;
1621 RTListForEach(&pThis->listHGCMCmd, pCmd, VBOXHGCMCMD, node)
1622 {
1623 LogFlowFunc(("pCmd %p\n", pCmd));
1624 ++cCmds;
1625 }
1626 LogFlowFunc(("cCmds = %d\n", cCmds));
1627
1628 /* Save number of commands. */
1629 int rc = SSMR3PutU32(pSSM, cCmds);
1630 AssertRCReturn(rc, rc);
1631
1632 if (cCmds > 0)
1633 {
1634 RTListForEach(&pThis->listHGCMCmd, pCmd, VBOXHGCMCMD, node)
1635 {
1636 LogFlowFunc(("Saving %RGp, size %d\n", pCmd->GCPhys, pCmd->cbRequest));
1637
1638 /** @todo Don't save cancelled requests! It serves no purpose. See restore and
1639 * @bugref{4032#c4} for details. */
1640 SSMR3PutU32 (pSSM, (uint32_t)pCmd->enmCmdType);
1641 SSMR3PutBool (pSSM, pCmd->fCancelled);
1642 SSMR3PutGCPhys (pSSM, pCmd->GCPhys);
1643 SSMR3PutU32 (pSSM, pCmd->cbRequest);
1644 SSMR3PutU32 (pSSM, (uint32_t)pCmd->enmRequestType);
1645 const uint32_t cParms = pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL ? pCmd->u.call.cParms : 0;
1646 rc = SSMR3PutU32(pSSM, cParms);
1647 AssertRCReturn(rc, rc);
1648
1649 if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL)
1650 {
1651 SSMR3PutU32 (pSSM, pCmd->u.call.u32ClientID);
1652 rc = SSMR3PutU32(pSSM, pCmd->u.call.u32Function);
1653 AssertRCReturn(rc, rc);
1654
1655 /* Guest parameters. */
1656 uint32_t i;
1657 for (i = 0; i < pCmd->u.call.cParms; ++i)
1658 {
1659 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
1660
1661 rc = SSMR3PutU32(pSSM, (uint32_t)pGuestParm->enmType);
1662 AssertRCReturn(rc, rc);
1663
1664 if ( pGuestParm->enmType == VMMDevHGCMParmType_32bit
1665 || pGuestParm->enmType == VMMDevHGCMParmType_64bit)
1666 {
1667 const VBOXHGCMPARMVAL * const pVal = &pGuestParm->u.val;
1668 SSMR3PutU64 (pSSM, pVal->u64Value);
1669 SSMR3PutU32 (pSSM, pVal->offValue);
1670 rc = SSMR3PutU32(pSSM, pVal->cbValue);
1671 }
1672 else if ( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In
1673 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out
1674 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr
1675 || pGuestParm->enmType == VMMDevHGCMParmType_PageList
1676 || pGuestParm->enmType == VMMDevHGCMParmType_Embedded
1677 || pGuestParm->enmType == VMMDevHGCMParmType_ContiguousPageList)
1678 {
1679 const VBOXHGCMPARMPTR * const pPtr = &pGuestParm->u.ptr;
1680 SSMR3PutU32 (pSSM, pPtr->cbData);
1681 SSMR3PutU32 (pSSM, pPtr->offFirstPage);
1682 SSMR3PutU32 (pSSM, pPtr->cPages);
1683 rc = SSMR3PutU32(pSSM, pPtr->fu32Direction);
1684
1685 uint32_t iPage;
1686 for (iPage = 0; iPage < pPtr->cPages; ++iPage)
1687 rc = SSMR3PutGCPhys(pSSM, pPtr->paPages[iPage]);
1688 }
1689 else
1690 {
1691 AssertFailedStmt(rc = VERR_INTERNAL_ERROR);
1692 }
1693 AssertRCReturn(rc, rc);
1694 }
1695 }
1696 else if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
1697 {
1698 SSMR3PutU32(pSSM, pCmd->u.connect.u32ClientID);
1699 SSMR3PutMem(pSSM, pCmd->u.connect.pLoc, sizeof(*pCmd->u.connect.pLoc));
1700 }
1701 else if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT)
1702 {
1703 SSMR3PutU32(pSSM, pCmd->u.disconnect.u32ClientID);
1704 }
1705 else
1706 {
1707 AssertFailedReturn(VERR_INTERNAL_ERROR);
1708 }
1709
1710 /* A reserved field, will allow to extend saved data for a command. */
1711 rc = SSMR3PutU32(pSSM, 0);
1712 AssertRCReturn(rc, rc);
1713 }
1714 }
1715
1716 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
1717 rc = SSMR3PutU32(pSSM, 0);
1718 AssertRCReturn(rc, rc);
1719
1720 return rc;
1721}
1722
1723/** Load information about pending HGCM requests.
1724 *
1725 * Allocate VBOXHGCMCMD commands and add them to pThis->listHGCMCmd temporarily.
1726 * vmmdevHGCMLoadStateDone will process the temporary list. This includes
1727 * loading the correct fRequestor fields.
1728 *
1729 * @returns VBox status code that the guest should see.
1730 * @param pThis The VMMDev instance data.
1731 * @param pSSM SSM handle for SSM functions.
1732 * @param uVersion Saved state version.
1733 *
1734 * @thread EMT
1735 */
1736int vmmdevHGCMLoadState(PVMMDEV pThis, PSSMHANDLE pSSM, uint32_t uVersion)
1737{
1738 LogFlowFunc(("\n"));
1739
1740 pThis->u32SSMVersion = uVersion; /* For vmmdevHGCMLoadStateDone */
1741
1742 /* Read how many commands were pending. */
1743 uint32_t cCmds = 0;
1744 int rc = SSMR3GetU32(pSSM, &cCmds);
1745 AssertRCReturn(rc, rc);
1746
1747 LogFlowFunc(("cCmds = %d\n", cCmds));
1748
1749 if (uVersion >= VMMDEV_SAVED_STATE_VERSION_HGCM_PARAMS)
1750 {
1751 /* Saved information about all HGCM parameters. */
1752 uint32_t u32;
1753
1754 uint32_t iCmd;
1755 for (iCmd = 0; iCmd < cCmds; ++iCmd)
1756 {
1757 /* Command fields. */
1758 VBOXHGCMCMDTYPE enmCmdType;
1759 bool fCancelled;
1760 RTGCPHYS GCPhys;
1761 uint32_t cbRequest;
1762 VMMDevRequestType enmRequestType;
1763 uint32_t cParms;
1764
1765 SSMR3GetU32 (pSSM, &u32);
1766 enmCmdType = (VBOXHGCMCMDTYPE)u32;
1767 SSMR3GetBool (pSSM, &fCancelled);
1768 SSMR3GetGCPhys (pSSM, &GCPhys);
1769 SSMR3GetU32 (pSSM, &cbRequest);
1770 SSMR3GetU32 (pSSM, &u32);
1771 enmRequestType = (VMMDevRequestType)u32;
1772 rc = SSMR3GetU32(pSSM, &cParms);
1773 AssertRCReturn(rc, rc);
1774
1775 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, enmCmdType, GCPhys, cbRequest, cParms, 0 /*fRequestor*/);
1776 AssertReturn(pCmd, VERR_NO_MEMORY);
1777
1778 pCmd->fCancelled = fCancelled;
1779 pCmd->GCPhys = GCPhys;
1780 pCmd->cbRequest = cbRequest;
1781 pCmd->enmRequestType = enmRequestType;
1782
1783 if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
1784 {
1785 SSMR3GetU32 (pSSM, &pCmd->u.call.u32ClientID);
1786 rc = SSMR3GetU32(pSSM, &pCmd->u.call.u32Function);
1787 AssertRCReturn(rc, rc);
1788
1789 /* Guest parameters. */
1790 uint32_t i;
1791 for (i = 0; i < cParms; ++i)
1792 {
1793 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[i];
1794
1795 rc = SSMR3GetU32(pSSM, &u32);
1796 AssertRCReturn(rc, rc);
1797 pGuestParm->enmType = (HGCMFunctionParameterType)u32;
1798
1799 if ( pGuestParm->enmType == VMMDevHGCMParmType_32bit
1800 || pGuestParm->enmType == VMMDevHGCMParmType_64bit)
1801 {
1802 VBOXHGCMPARMVAL * const pVal = &pGuestParm->u.val;
1803 SSMR3GetU64 (pSSM, &pVal->u64Value);
1804 SSMR3GetU32 (pSSM, &pVal->offValue);
1805 rc = SSMR3GetU32(pSSM, &pVal->cbValue);
1806 }
1807 else if ( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In
1808 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out
1809 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr
1810 || pGuestParm->enmType == VMMDevHGCMParmType_PageList
1811 || pGuestParm->enmType == VMMDevHGCMParmType_Embedded
1812 || pGuestParm->enmType == VMMDevHGCMParmType_ContiguousPageList)
1813 {
1814 VBOXHGCMPARMPTR * const pPtr = &pGuestParm->u.ptr;
1815 SSMR3GetU32 (pSSM, &pPtr->cbData);
1816 SSMR3GetU32 (pSSM, &pPtr->offFirstPage);
1817 SSMR3GetU32 (pSSM, &pPtr->cPages);
1818 rc = SSMR3GetU32(pSSM, &pPtr->fu32Direction);
1819 if (RT_SUCCESS(rc))
1820 {
1821 if (pPtr->cPages == 1)
1822 pPtr->paPages = &pPtr->GCPhysSinglePage;
1823 else
1824 {
1825 AssertReturn( pGuestParm->enmType != VMMDevHGCMParmType_Embedded
1826 && pGuestParm->enmType != VMMDevHGCMParmType_ContiguousPageList, VERR_INTERNAL_ERROR_3);
1827 pPtr->paPages = (RTGCPHYS *)RTMemAlloc(pPtr->cPages * sizeof(RTGCPHYS));
1828 AssertStmt(pPtr->paPages, rc = VERR_NO_MEMORY);
1829 }
1830
1831 if (RT_SUCCESS(rc))
1832 {
1833 uint32_t iPage;
1834 for (iPage = 0; iPage < pPtr->cPages; ++iPage)
1835 rc = SSMR3GetGCPhys(pSSM, &pPtr->paPages[iPage]);
1836 }
1837 }
1838 }
1839 else
1840 {
1841 AssertFailedStmt(rc = VERR_INTERNAL_ERROR);
1842 }
1843 AssertRCReturn(rc, rc);
1844 }
1845 }
1846 else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
1847 {
1848 SSMR3GetU32(pSSM, &pCmd->u.connect.u32ClientID);
1849 rc = SSMR3GetMem(pSSM, pCmd->u.connect.pLoc, sizeof(*pCmd->u.connect.pLoc));
1850 AssertRCReturn(rc, rc);
1851 }
1852 else if (enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT)
1853 {
1854 rc = SSMR3GetU32(pSSM, &pCmd->u.disconnect.u32ClientID);
1855 AssertRCReturn(rc, rc);
1856 }
1857 else
1858 {
1859 AssertFailedReturn(VERR_INTERNAL_ERROR);
1860 }
1861
1862 /* A reserved field, will allow to extend saved data for a command. */
1863 rc = SSMR3GetU32(pSSM, &u32);
1864 AssertRCReturn(rc, rc);
1865
1866 /*
1867 * Do not restore cancelled calls. Why do we save them to start with?
1868 *
1869 * The guest memory no longer contains a valid request! So, it is not
1870 * possible to restore it. The memory is often reused for a new request
1871 * by now and we will end up trying to complete that more than once if
1872 * we restore a cancelled call. In some cases VERR_HGCM_INVALID_CLIENT_ID
1873 * is returned, though it might just be silent memory corruption.
1874 */
1875 /* See current version above. */
1876 if (!fCancelled)
1877 vmmdevHGCMAddCommand(pThis, pCmd);
1878 else
1879 {
1880 Log(("vmmdevHGCMLoadState: Skipping cancelled request: enmCmdType=%d GCPhys=%#RX32 LB %#x\n",
1881 enmCmdType, GCPhys, cbRequest));
1882 vmmdevHGCMCmdFree(pThis, pCmd);
1883 }
1884 }
1885
1886 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
1887 rc = SSMR3GetU32(pSSM, &u32);
1888 AssertRCReturn(rc, rc);
1889 }
1890 else if (uVersion >= 9)
1891 {
1892 /* Version 9+: Load information about commands. Pre-rewrite. */
1893 uint32_t u32;
1894
1895 uint32_t iCmd;
1896 for (iCmd = 0; iCmd < cCmds; ++iCmd)
1897 {
1898 VBOXHGCMCMDTYPE enmCmdType;
1899 bool fCancelled;
1900 RTGCPHYS GCPhys;
1901 uint32_t cbRequest;
1902 uint32_t cLinAddrs;
1903
1904 SSMR3GetGCPhys (pSSM, &GCPhys);
1905 rc = SSMR3GetU32(pSSM, &cbRequest);
1906 AssertRCReturn(rc, rc);
1907
1908 LogFlowFunc(("Restoring %RGp size %x bytes\n", GCPhys, cbRequest));
1909
1910 /* For uVersion <= 12, this was the size of entire command.
1911 * Now the command is reconstructed in vmmdevHGCMLoadStateDone.
1912 */
1913 if (uVersion <= 12)
1914 SSMR3Skip(pSSM, sizeof (uint32_t));
1915
1916 SSMR3GetU32 (pSSM, &u32);
1917 enmCmdType = (VBOXHGCMCMDTYPE)u32;
1918 SSMR3GetBool (pSSM, &fCancelled);
1919 /* How many linear pointers. Always 0 if not a call command. */
1920 rc = SSMR3GetU32(pSSM, &cLinAddrs);
1921 AssertRCReturn(rc, rc);
1922
1923 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, enmCmdType, GCPhys, cbRequest, cLinAddrs, 0 /*fRequestor*/);
1924 AssertReturn(pCmd, VERR_NO_MEMORY);
1925
1926 pCmd->fCancelled = fCancelled;
1927 pCmd->GCPhys = GCPhys;
1928 pCmd->cbRequest = cbRequest;
1929
1930 if (cLinAddrs > 0)
1931 {
1932 /* Skip number of pages for all LinAddrs in this command. */
1933 SSMR3Skip(pSSM, sizeof(uint32_t));
1934
1935 uint32_t i;
1936 for (i = 0; i < cLinAddrs; ++i)
1937 {
1938 VBOXHGCMPARMPTR * const pPtr = &pCmd->u.call.paGuestParms[i].u.ptr;
1939
1940 /* Index of the parameter. Use cbData field to store the index. */
1941 SSMR3GetU32 (pSSM, &pPtr->cbData);
1942 SSMR3GetU32 (pSSM, &pPtr->offFirstPage);
1943 rc = SSMR3GetU32(pSSM, &pPtr->cPages);
1944 AssertRCReturn(rc, rc);
1945
1946 pPtr->paPages = (RTGCPHYS *)RTMemAlloc(pPtr->cPages * sizeof(RTGCPHYS));
1947 AssertReturn(pPtr->paPages, VERR_NO_MEMORY);
1948
1949 uint32_t iPage;
1950 for (iPage = 0; iPage < pPtr->cPages; ++iPage)
1951 rc = SSMR3GetGCPhys(pSSM, &pPtr->paPages[iPage]);
1952 }
1953 }
1954
1955 /* A reserved field, will allow to extend saved data for a command. */
1956 rc = SSMR3GetU32(pSSM, &u32);
1957 AssertRCReturn(rc, rc);
1958
1959 /* See current version above. */
1960 if (!fCancelled)
1961 vmmdevHGCMAddCommand(pThis, pCmd);
1962 else
1963 {
1964 Log(("vmmdevHGCMLoadState: Skipping cancelled request: enmCmdType=%d GCPhys=%#RX32 LB %#x\n",
1965 enmCmdType, GCPhys, cbRequest));
1966 vmmdevHGCMCmdFree(pThis, pCmd);
1967 }
1968 }
1969
1970 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
1971 rc = SSMR3GetU32(pSSM, &u32);
1972 AssertRCReturn(rc, rc);
1973 }
1974 else
1975 {
1976 /* Ancient. Only the guest physical address is saved. */
1977 uint32_t iCmd;
1978 for (iCmd = 0; iCmd < cCmds; ++iCmd)
1979 {
1980 RTGCPHYS GCPhys;
1981 uint32_t cbRequest;
1982
1983 SSMR3GetGCPhys(pSSM, &GCPhys);
1984 rc = SSMR3GetU32(pSSM, &cbRequest);
1985 AssertRCReturn(rc, rc);
1986
1987 LogFlowFunc(("Restoring %RGp size %x bytes\n", GCPhys, cbRequest));
1988
1989 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_LOADSTATE, GCPhys, cbRequest, 0, 0 /*fRequestor*/);
1990 AssertReturn(pCmd, VERR_NO_MEMORY);
1991
1992 vmmdevHGCMAddCommand(pThis, pCmd);
1993 }
1994 }
1995
1996 return rc;
1997}
1998
1999/** Restore HGCM connect command loaded from old saved state.
2000 *
2001 * @returns VBox status code that the guest should see.
2002 * @param pThis The VMMDev instance data.
2003 * @param u32SSMVersion The saved state version the command has been loaded from.
2004 * @param pLoadedCmd Command loaded from saved state, it is imcomplete and needs restoration.
2005 * @param pReq The guest request (cached in host memory).
2006 * @param cbReq Size of the guest request.
2007 * @param enmRequestType Type of the HGCM request.
2008 * @param ppRestoredCmd Where to store pointer to newly allocated restored command.
2009 */
2010static int vmmdevHGCMRestoreConnect(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
2011 VMMDevHGCMConnect *pReq, uint32_t cbReq, VMMDevRequestType enmRequestType,
2012 VBOXHGCMCMD **ppRestoredCmd)
2013{
2014 RT_NOREF(pThis);
2015
2016 int rc = VINF_SUCCESS;
2017
2018 /* Verify the request. */
2019 ASSERT_GUEST_RETURN(cbReq >= sizeof(*pReq), VERR_MISMATCH);
2020 if (u32SSMVersion >= 9)
2021 ASSERT_GUEST_RETURN(pLoadedCmd->enmCmdType == VBOXHGCMCMDTYPE_CONNECT, VERR_MISMATCH);
2022
2023 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_CONNECT, pLoadedCmd->GCPhys, cbReq, 0,
2024 pReq->header.header.fRequestor);
2025 AssertReturn(pCmd, VERR_NO_MEMORY);
2026
2027 Assert(pLoadedCmd->fCancelled == false);
2028 pCmd->fCancelled = false;
2029 pCmd->fRestored = true;
2030 pCmd->enmRequestType = enmRequestType;
2031
2032 vmmdevHGCMConnectFetch(pReq, pCmd);
2033
2034 if (RT_SUCCESS(rc))
2035 *ppRestoredCmd = pCmd;
2036
2037 return rc;
2038}
2039
2040/** Restore HGCM disconnect command loaded from old saved state.
2041 *
2042 * @returns VBox status code that the guest should see.
2043 * @param pThis The VMMDev instance data.
2044 * @param u32SSMVersion The saved state version the command has been loaded from.
2045 * @param pLoadedCmd Command loaded from saved state, it is imcomplete and needs restoration.
2046 * @param pReq The guest request (cached in host memory).
2047 * @param cbReq Size of the guest request.
2048 * @param enmRequestType Type of the HGCM request.
2049 * @param ppRestoredCmd Where to store pointer to newly allocated restored command.
2050 */
2051static int vmmdevHGCMRestoreDisconnect(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
2052 VMMDevHGCMDisconnect *pReq, uint32_t cbReq, VMMDevRequestType enmRequestType,
2053 VBOXHGCMCMD **ppRestoredCmd)
2054{
2055 RT_NOREF(pThis);
2056
2057 int rc = VINF_SUCCESS;
2058
2059 /* Verify the request. */
2060 ASSERT_GUEST_RETURN(cbReq >= sizeof(*pReq), VERR_MISMATCH);
2061 if (u32SSMVersion >= 9)
2062 ASSERT_GUEST_RETURN(pLoadedCmd->enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT, VERR_MISMATCH);
2063
2064 PVBOXHGCMCMD pCmd = vmmdevHGCMCmdAlloc(pThis, VBOXHGCMCMDTYPE_DISCONNECT, pLoadedCmd->GCPhys, cbReq, 0,
2065 pReq->header.header.fRequestor);
2066 AssertReturn(pCmd, VERR_NO_MEMORY);
2067
2068 Assert(pLoadedCmd->fCancelled == false);
2069 pCmd->fCancelled = false;
2070 pCmd->fRestored = true;
2071 pCmd->enmRequestType = enmRequestType;
2072
2073 vmmdevHGCMDisconnectFetch(pReq, pCmd);
2074
2075 if (RT_SUCCESS(rc))
2076 *ppRestoredCmd = pCmd;
2077
2078 return rc;
2079}
2080
2081/** Restore HGCM call command loaded from old saved state.
2082 *
2083 * @returns VBox status code that the guest should see.
2084 * @param pThis The VMMDev instance data.
2085 * @param u32SSMVersion The saved state version the command has been loaded from.
2086 * @param pLoadedCmd Command loaded from saved state, it is imcomplete and needs restoration.
2087 * @param pReq The guest request (cached in host memory).
2088 * @param cbReq Size of the guest request.
2089 * @param enmRequestType Type of the HGCM request.
2090 * @param ppRestoredCmd Where to store pointer to newly allocated restored command.
2091 */
2092static int vmmdevHGCMRestoreCall(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
2093 VMMDevHGCMCall *pReq, uint32_t cbReq, VMMDevRequestType enmRequestType,
2094 VBOXHGCMCMD **ppRestoredCmd)
2095{
2096 int rc = VINF_SUCCESS;
2097
2098 /* Verify the request. */
2099 ASSERT_GUEST_RETURN(cbReq >= sizeof(*pReq), VERR_MISMATCH);
2100 if (u32SSMVersion >= 9)
2101 {
2102 ASSERT_GUEST_RETURN(pLoadedCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL, VERR_MISMATCH);
2103 Assert(pLoadedCmd->fCancelled == false);
2104 }
2105
2106 PVBOXHGCMCMD pCmd;
2107 uint32_t cbHGCMParmStruct;
2108 rc = vmmdevHGCMCallAlloc(pThis, pReq, cbReq, pLoadedCmd->GCPhys, enmRequestType, &pCmd, &cbHGCMParmStruct);
2109 if (RT_FAILURE(rc))
2110 return rc;
2111
2112 /* pLoadedCmd is fake, it does not contain actual call parameters. Only pagelists for LinAddr. */
2113 pCmd->fCancelled = false;
2114 pCmd->fRestored = true;
2115 pCmd->enmRequestType = enmRequestType;
2116
2117 rc = vmmdevHGCMCallFetchGuestParms(pThis, pCmd, pReq, cbReq, enmRequestType, cbHGCMParmStruct);
2118 if (RT_SUCCESS(rc))
2119 {
2120 /* Update LinAddr parameters from pLoadedCmd.
2121 * pLoadedCmd->u.call.cParms is actually the number of LinAddrs, see vmmdevHGCMLoadState.
2122 */
2123 uint32_t iLinAddr;
2124 for (iLinAddr = 0; iLinAddr < pLoadedCmd->u.call.cParms; ++iLinAddr)
2125 {
2126 VBOXHGCMGUESTPARM * const pLoadedParm = &pLoadedCmd->u.call.paGuestParms[iLinAddr];
2127 /* pLoadedParm->cbData is actually index of the LinAddr parameter, see vmmdevHGCMLoadState. */
2128 const uint32_t iParm = pLoadedParm->u.ptr.cbData;
2129 ASSERT_GUEST_STMT_BREAK(iParm < pCmd->u.call.cParms, rc = VERR_MISMATCH);
2130
2131 VBOXHGCMGUESTPARM * const pGuestParm = &pCmd->u.call.paGuestParms[iParm];
2132 ASSERT_GUEST_STMT_BREAK( pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_In
2133 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr_Out
2134 || pGuestParm->enmType == VMMDevHGCMParmType_LinAddr,
2135 rc = VERR_MISMATCH);
2136 ASSERT_GUEST_STMT_BREAK( pLoadedParm->u.ptr.offFirstPage == pGuestParm->u.ptr.offFirstPage
2137 && pLoadedParm->u.ptr.cPages == pGuestParm->u.ptr.cPages,
2138 rc = VERR_MISMATCH);
2139 memcpy(pGuestParm->u.ptr.paPages, pLoadedParm->u.ptr.paPages, pGuestParm->u.ptr.cPages * sizeof(RTGCPHYS));
2140 }
2141 }
2142
2143 if (RT_SUCCESS(rc))
2144 *ppRestoredCmd = pCmd;
2145 else
2146 vmmdevHGCMCmdFree(pThis, pCmd);
2147
2148 return rc;
2149}
2150
2151/** Allocate and initialize a HGCM command using the given request (pReqHdr)
2152 * and command loaded from saved state (pCmd).
2153 *
2154 * @returns VBox status code that the guest should see.
2155 * @param pThis The VMMDev instance data.
2156 * @param u32SSMVersion Saved state version.
2157 * @param pLoadedCmd HGCM command which needs restoration.
2158 * @param pReqHdr The request (cached in host memory).
2159 * @param cbReq Size of the entire request (including HGCM parameters).
2160 * @param ppRestoredCmd Where to store pointer to restored command.
2161 */
2162static int vmmdevHGCMRestoreCommand(PVMMDEV pThis, uint32_t u32SSMVersion, const VBOXHGCMCMD *pLoadedCmd,
2163 const VMMDevHGCMRequestHeader *pReqHdr, uint32_t cbReq,
2164 VBOXHGCMCMD **ppRestoredCmd)
2165{
2166 int rc = VINF_SUCCESS;
2167
2168 /* Verify the request. */
2169 ASSERT_GUEST_RETURN(cbReq >= sizeof(VMMDevHGCMRequestHeader), VERR_MISMATCH);
2170 ASSERT_GUEST_RETURN(cbReq == pReqHdr->header.size, VERR_MISMATCH);
2171
2172 const VMMDevRequestType enmRequestType = pReqHdr->header.requestType;
2173 switch (enmRequestType)
2174 {
2175 case VMMDevReq_HGCMConnect:
2176 {
2177 VMMDevHGCMConnect *pReq = (VMMDevHGCMConnect *)pReqHdr;
2178 rc = vmmdevHGCMRestoreConnect(pThis, u32SSMVersion, pLoadedCmd, pReq, cbReq, enmRequestType,
2179 ppRestoredCmd);
2180 break;
2181 }
2182
2183 case VMMDevReq_HGCMDisconnect:
2184 {
2185 VMMDevHGCMDisconnect *pReq = (VMMDevHGCMDisconnect *)pReqHdr;
2186 rc = vmmdevHGCMRestoreDisconnect(pThis, u32SSMVersion, pLoadedCmd, pReq, cbReq, enmRequestType,
2187 ppRestoredCmd);
2188 break;
2189 }
2190
2191#ifdef VBOX_WITH_64_BITS_GUESTS
2192 case VMMDevReq_HGCMCall32:
2193 case VMMDevReq_HGCMCall64:
2194#else
2195 case VMMDevReq_HGCMCall:
2196#endif
2197 {
2198 VMMDevHGCMCall *pReq = (VMMDevHGCMCall *)pReqHdr;
2199 rc = vmmdevHGCMRestoreCall(pThis, u32SSMVersion, pLoadedCmd, pReq, cbReq, enmRequestType,
2200 ppRestoredCmd);
2201 break;
2202 }
2203
2204 default:
2205 ASSERT_GUEST_FAILED_RETURN(VERR_MISMATCH);
2206 }
2207
2208 return rc;
2209}
2210
2211/** Resubmit pending HGCM commands which were loaded form saved state.
2212 *
2213 * @returns VBox status code.
2214 * @param pThis The VMMDev instance data.
2215 *
2216 * @thread EMT
2217 */
2218int vmmdevHGCMLoadStateDone(PVMMDEV pThis)
2219{
2220 /*
2221 * Resubmit pending HGCM commands to services.
2222 *
2223 * pThis->pHGCMCmdList contains commands loaded by vmmdevHGCMLoadState.
2224 *
2225 * Legacy saved states (pre VMMDEV_SAVED_STATE_VERSION_HGCM_PARAMS)
2226 * do not have enough information about the command parameters,
2227 * therefore it is necessary to reload at least some data from the
2228 * guest memory to construct commands.
2229 *
2230 * There are two types of legacy saved states which contain:
2231 * 1) the guest physical address and size of request;
2232 * 2) additionally page lists for LinAddr parameters.
2233 *
2234 * Legacy commands have enmCmdType = VBOXHGCMCMDTYPE_LOADSTATE?
2235 */
2236
2237 int rcFunc = VINF_SUCCESS; /* This status code will make the function fail. I.e. VM will not start. */
2238
2239 /* Get local copy of the list of loaded commands. */
2240 RTLISTANCHOR listLoadedCommands;
2241 RTListMove(&listLoadedCommands, &pThis->listHGCMCmd);
2242
2243 /* Resubmit commands. */
2244 PVBOXHGCMCMD pCmd, pNext;
2245 RTListForEachSafe(&listLoadedCommands, pCmd, pNext, VBOXHGCMCMD, node)
2246 {
2247 int rcCmd = VINF_SUCCESS; /* This status code will make the HGCM command fail for the guest. */
2248
2249 RTListNodeRemove(&pCmd->node);
2250
2251 /*
2252 * Re-read the request from the guest memory.
2253 * It will be used to:
2254 * * reconstruct commands if legacy saved state has been restored;
2255 * * report an error to the guest if resubmit failed.
2256 */
2257 VMMDevHGCMRequestHeader *pReqHdr = (VMMDevHGCMRequestHeader *)RTMemAlloc(pCmd->cbRequest);
2258 AssertBreakStmt(pReqHdr, vmmdevHGCMCmdFree(pThis, pCmd); rcFunc = VERR_NO_MEMORY);
2259
2260 PDMDevHlpPhysRead(pThis->pDevInsR3, pCmd->GCPhys, pReqHdr, pCmd->cbRequest);
2261 RT_UNTRUSTED_NONVOLATILE_COPY_FENCE();
2262
2263 if (pThis->pHGCMDrv)
2264 {
2265 /*
2266 * Reconstruct legacy commands.
2267 */
2268 if (RT_LIKELY(pThis->u32SSMVersion >= VMMDEV_SAVED_STATE_VERSION_HGCM_PARAMS))
2269 { /* likely */ }
2270 else
2271 {
2272 PVBOXHGCMCMD pRestoredCmd = NULL;
2273 rcCmd = vmmdevHGCMRestoreCommand(pThis, pThis->u32SSMVersion, pCmd,
2274 pReqHdr, pCmd->cbRequest, &pRestoredCmd);
2275 if (RT_SUCCESS(rcCmd))
2276 {
2277 Assert(pCmd != pRestoredCmd); /* vmmdevHGCMRestoreCommand must allocate restored command. */
2278 vmmdevHGCMCmdFree(pThis, pCmd);
2279 pCmd = pRestoredCmd;
2280 }
2281 }
2282
2283 /* Resubmit commands. */
2284 if (RT_SUCCESS(rcCmd))
2285 {
2286 switch (pCmd->enmCmdType)
2287 {
2288 case VBOXHGCMCMDTYPE_CONNECT:
2289 {
2290 vmmdevHGCMAddCommand(pThis, pCmd);
2291 rcCmd = pThis->pHGCMDrv->pfnConnect(pThis->pHGCMDrv, pCmd, pCmd->u.connect.pLoc,
2292 &pCmd->u.connect.u32ClientID);
2293 if (RT_FAILURE(rcCmd))
2294 vmmdevHGCMRemoveCommand(pThis, pCmd);
2295 break;
2296 }
2297
2298 case VBOXHGCMCMDTYPE_DISCONNECT:
2299 {
2300 vmmdevHGCMAddCommand(pThis, pCmd);
2301 rcCmd = pThis->pHGCMDrv->pfnDisconnect(pThis->pHGCMDrv, pCmd, pCmd->u.disconnect.u32ClientID);
2302 if (RT_FAILURE(rcCmd))
2303 vmmdevHGCMRemoveCommand(pThis, pCmd);
2304 break;
2305 }
2306
2307 case VBOXHGCMCMDTYPE_CALL:
2308 {
2309 rcCmd = vmmdevHGCMInitHostParameters(pThis, pCmd, (uint8_t const *)pReqHdr);
2310 if (RT_SUCCESS(rcCmd))
2311 {
2312 vmmdevHGCMAddCommand(pThis, pCmd);
2313
2314 /* Pass the function call to HGCM connector for actual processing */
2315 uint64_t tsNow;
2316 STAM_GET_TS(tsNow);
2317 rcCmd = pThis->pHGCMDrv->pfnCall(pThis->pHGCMDrv, pCmd,
2318 pCmd->u.call.u32ClientID, pCmd->u.call.u32Function,
2319 pCmd->u.call.cParms, pCmd->u.call.paHostParms, tsNow);
2320 if (RT_FAILURE(rcCmd))
2321 {
2322 LogFunc(("pfnCall rc = %Rrc\n", rcCmd));
2323 vmmdevHGCMRemoveCommand(pThis, pCmd);
2324 }
2325 }
2326 break;
2327 }
2328
2329 default:
2330 AssertFailedStmt(rcCmd = VERR_INTERNAL_ERROR);
2331 }
2332 }
2333 }
2334 else
2335 AssertFailedStmt(rcCmd = VERR_INTERNAL_ERROR);
2336
2337 if (RT_SUCCESS(rcCmd))
2338 { /* likely */ }
2339 else
2340 {
2341 /* Return the error to the guest. Guest may try to repeat the call. */
2342 pReqHdr->result = rcCmd;
2343 pReqHdr->header.rc = rcCmd;
2344 pReqHdr->fu32Flags |= VBOX_HGCM_REQ_DONE;
2345
2346 /* Write back only the header. */
2347 PDMDevHlpPhysWrite(pThis->pDevInsR3, pCmd->GCPhys, pReqHdr, sizeof(*pReqHdr));
2348
2349 VMMDevNotifyGuest(pThis, VMMDEV_EVENT_HGCM);
2350
2351 /* Deallocate the command memory. */
2352 vmmdevHGCMCmdFree(pThis, pCmd);
2353 }
2354
2355 RTMemFree(pReqHdr);
2356 }
2357
2358 if (RT_FAILURE(rcFunc))
2359 {
2360 RTListForEachSafe(&listLoadedCommands, pCmd, pNext, VBOXHGCMCMD, node)
2361 {
2362 RTListNodeRemove(&pCmd->node);
2363 vmmdevHGCMCmdFree(pThis, pCmd);
2364 }
2365 }
2366
2367 return rcFunc;
2368}
2369
2370
2371/**
2372 * Counterpart to vmmdevHGCMInit().
2373 *
2374 * @param pThis The VMMDev instance data.
2375 */
2376void vmmdevHGCMDestroy(PVMMDEV pThis)
2377{
2378 LogFlowFunc(("\n"));
2379
2380 if (RTCritSectIsInitialized(&pThis->critsectHGCMCmdList))
2381 {
2382 PVBOXHGCMCMD pCmd, pNext;
2383 RTListForEachSafe(&pThis->listHGCMCmd, pCmd, pNext, VBOXHGCMCMD, node)
2384 {
2385 vmmdevHGCMRemoveCommand(pThis, pCmd);
2386 vmmdevHGCMCmdFree(pThis, pCmd);
2387 }
2388
2389 RTCritSectDelete(&pThis->critsectHGCMCmdList);
2390 }
2391
2392 AssertCompile((uintptr_t)NIL_RTMEMCACHE == 0);
2393 if (pThis->hHgcmCmdCache != NIL_RTMEMCACHE)
2394 {
2395 RTMemCacheDestroy(pThis->hHgcmCmdCache);
2396 pThis->hHgcmCmdCache = NIL_RTMEMCACHE;
2397 }
2398}
2399
2400
2401/**
2402 * Initializes the HGCM specific state.
2403 *
2404 * Keeps VBOXHGCMCMDCACHED and friends local.
2405 *
2406 * @returns VBox status code.
2407 * @param pThis The VMMDev instance data.
2408 */
2409int vmmdevHGCMInit(PVMMDEV pThis)
2410{
2411 LogFlowFunc(("\n"));
2412
2413 RTListInit(&pThis->listHGCMCmd);
2414
2415 int rc = RTCritSectInit(&pThis->critsectHGCMCmdList);
2416 AssertLogRelRCReturn(rc, rc);
2417
2418 rc = RTMemCacheCreate(&pThis->hHgcmCmdCache, sizeof(VBOXHGCMCMDCACHED), 64, _1M, NULL, NULL, NULL, 0);
2419 AssertLogRelRCReturn(rc, rc);
2420
2421 pThis->u32HGCMEnabled = 0;
2422
2423 return VINF_SUCCESS;
2424}
2425
Note: See TracBrowser for help on using the repository browser.

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