VirtualBox

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

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

VMMDev: Keep a 4K buffer around for each EMT to read the VMM request into, only hit the heap for larger buffers. Enabled the request locking for HGCM calls. bugref:9172

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