VirtualBox

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

Last change on this file since 71963 was 71931, checked in by vboxsync, 7 years ago

VMMDev: fixed restoring VMs which do not use HGCM.

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