VirtualBox

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

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

VMMDev,Main,HostServices: More profiling of HGCM guest call processing.

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