VirtualBox

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

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

VMMDev/HGCM: Added PDM interface + HGCM server helper for finding out if a command/call is being resubmitted on restore or not. This is handy for returning returning an async wait call to the guest upon restore. bugref:3544

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

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