VirtualBox

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

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

VMMDevHGCM: Fixed bug in copy-back of the new VMMDevHGCMParmType_ContiguousPageList parameter type. bugref:9172

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