VirtualBox

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

Last change on this file since 46845 was 44792, checked in by vboxsync, 12 years ago

Some nits, use RT_FROM_MEMBER now that we've finally got it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 103.4 KB
Line 
1/* $Id: VMMDevHGCM.cpp 44792 2013-02-21 18:14:07Z vboxsync $ */
2/** @file
3 * VMMDev - HGCM - Host-Guest Communication Manager Device.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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/err.h>
30#include <VBox/hgcmsvc.h>
31
32#include <VBox/log.h>
33
34#include "VMMDevHGCM.h"
35
36#ifdef VBOX_WITH_DTRACE
37# include "dtrace/VBoxDD.h"
38#else
39# define VBOXDD_HGCMCALL_ENTER(a,b,c,d) do { } while (0)
40# define VBOXDD_HGCMCALL_COMPLETED_REQ(a,b) do { } while (0)
41# define VBOXDD_HGCMCALL_COMPLETED_EMT(a,b) do { } while (0)
42# define VBOXDD_HGCMCALL_COMPLETED_DONE(a,b,c,d) do { } while (0)
43#endif
44
45
46/*******************************************************************************
47* Structures and Typedefs *
48*******************************************************************************/
49typedef enum VBOXHGCMCMDTYPE
50{
51 VBOXHGCMCMDTYPE_LOADSTATE = 0,
52 VBOXHGCMCMDTYPE_CONNECT,
53 VBOXHGCMCMDTYPE_DISCONNECT,
54 VBOXHGCMCMDTYPE_CALL,
55 VBOXHGCMCMDTYPE_SizeHack = 0x7fffffff
56} VBOXHGCMCMDTYPE;
57
58/**
59 * Information about a linear ptr parameter.
60 */
61typedef struct VBOXHGCMLINPTR
62{
63 /** Index of the parameter. */
64 uint32_t iParm;
65
66 /** Offset in the first physical page of the region. */
67 uint32_t offFirstPage;
68
69 /** How many pages. */
70 uint32_t cPages;
71
72 /** Pointer to array of the GC physical addresses for these pages.
73 * It is assumed that the physical address of the locked resident guest page
74 * does not change.
75 */
76 RTGCPHYS *paPages;
77
78} VBOXHGCMLINPTR;
79
80struct VBOXHGCMCMD
81{
82 /** Active commands, list is protected by critsectHGCMCmdList. */
83 struct VBOXHGCMCMD *pNext;
84 struct VBOXHGCMCMD *pPrev;
85
86 /** The type of the command. */
87 VBOXHGCMCMDTYPE enmCmdType;
88
89 /** Whether the command was cancelled by the guest. */
90 bool fCancelled;
91
92 /** Whether the command is in the active commands list. */
93 bool fInList;
94
95 /** Whether the command was saved. */
96 bool fSaved;
97
98 /** GC physical address of the guest request. */
99 RTGCPHYS GCPhys;
100
101 /** Request packet size */
102 uint32_t cbSize;
103
104 /** Pointer to converted host parameters in case of a Call request.
105 * Parameters follow this structure in the same memory block.
106 */
107 VBOXHGCMSVCPARM *paHostParms;
108
109 /** Linear pointer parameters information. */
110 int cLinPtrs;
111
112 /** How many pages for all linptrs of this command.
113 * Only valid if cLinPtrs > 0. This field simplifies loading of saved state.
114 */
115 int cLinPtrPages;
116
117 /** Pointer to descriptions of linear pointers. */
118 VBOXHGCMLINPTR *paLinPtrs;
119};
120
121
122
123static int vmmdevHGCMCmdListLock (PVMMDEV pThis)
124{
125 int rc = RTCritSectEnter (&pThis->critsectHGCMCmdList);
126 AssertRC (rc);
127 return rc;
128}
129
130static void vmmdevHGCMCmdListUnlock (PVMMDEV pThis)
131{
132 int rc = RTCritSectLeave (&pThis->critsectHGCMCmdList);
133 AssertRC (rc);
134}
135
136static int vmmdevHGCMAddCommand (PVMMDEV pThis, PVBOXHGCMCMD pCmd, RTGCPHYS GCPhys, uint32_t cbSize, VBOXHGCMCMDTYPE enmCmdType)
137{
138 /* PPDMDEVINS pDevIns = pThis->pDevIns; */
139
140 int rc = vmmdevHGCMCmdListLock (pThis);
141
142 if (RT_SUCCESS (rc))
143 {
144 LogFlowFunc(("%p type %d\n", pCmd, enmCmdType));
145
146 /* Insert at the head of the list. The vmmdevHGCMLoadStateDone depends on this. */
147 pCmd->pNext = pThis->pHGCMCmdList;
148 pCmd->pPrev = NULL;
149
150 if (pThis->pHGCMCmdList)
151 {
152 pThis->pHGCMCmdList->pPrev = pCmd;
153 }
154
155 pThis->pHGCMCmdList = pCmd;
156
157 pCmd->fInList = true;
158
159 if (enmCmdType != VBOXHGCMCMDTYPE_LOADSTATE)
160 {
161 /* Loaded commands already have the right type. */
162 pCmd->enmCmdType = enmCmdType;
163 }
164 pCmd->GCPhys = GCPhys;
165 pCmd->cbSize = cbSize;
166
167 /* Automatically enable HGCM events, if there are HGCM commands. */
168 if ( enmCmdType == VBOXHGCMCMDTYPE_CONNECT
169 || enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT
170 || enmCmdType == VBOXHGCMCMDTYPE_CALL)
171 {
172 Log(("vmmdevHGCMAddCommand: u32HGCMEnabled = %d\n", pThis->u32HGCMEnabled));
173 if (ASMAtomicCmpXchgU32(&pThis->u32HGCMEnabled, 1, 0))
174 {
175 VMMDevCtlSetGuestFilterMask (pThis, VMMDEV_EVENT_HGCM, 0);
176 }
177 }
178
179 vmmdevHGCMCmdListUnlock (pThis);
180 }
181
182 return rc;
183}
184
185static int vmmdevHGCMRemoveCommand (PVMMDEV pThis, PVBOXHGCMCMD pCmd)
186{
187 /* PPDMDEVINS pDevIns = pThis->pDevIns; */
188
189 int rc = vmmdevHGCMCmdListLock (pThis);
190
191 if (RT_SUCCESS (rc))
192 {
193 LogFlowFunc(("%p\n", pCmd));
194
195 if (!pCmd->fInList)
196 {
197 LogFlowFunc(("%p not in the list\n", pCmd));
198 vmmdevHGCMCmdListUnlock (pThis);
199 return VINF_SUCCESS;
200 }
201
202 if (pCmd->pNext)
203 {
204 pCmd->pNext->pPrev = pCmd->pPrev;
205 }
206 else
207 {
208 /* Tail, do nothing. */
209 }
210
211 if (pCmd->pPrev)
212 {
213 pCmd->pPrev->pNext = pCmd->pNext;
214 }
215 else
216 {
217 pThis->pHGCMCmdList = pCmd->pNext;
218 }
219
220 pCmd->pNext = NULL;
221 pCmd->pPrev = NULL;
222 pCmd->fInList = false;
223
224 vmmdevHGCMCmdListUnlock (pThis);
225 }
226
227 return rc;
228}
229
230
231/**
232 * Find a HGCM command by its physical address.
233 *
234 * The caller is responsible for taking the command list lock before calling
235 * this function.
236 *
237 * @returns Pointer to the command on success, NULL otherwise.
238 * @param pThis The VMMDev instance data.
239 * @param GCPhys The physical address of the command we're looking
240 * for.
241 */
242DECLINLINE(PVBOXHGCMCMD) vmmdevHGCMFindCommandLocked (PVMMDEV pThis, RTGCPHYS GCPhys)
243{
244 for (PVBOXHGCMCMD pCmd = pThis->pHGCMCmdList;
245 pCmd;
246 pCmd = pCmd->pNext)
247 {
248 if (pCmd->GCPhys == GCPhys)
249 return pCmd;
250 }
251 return NULL;
252}
253
254static int vmmdevHGCMSaveLinPtr (PPDMDEVINS pDevIns,
255 uint32_t iParm,
256 RTGCPTR GCPtr,
257 uint32_t u32Size,
258 uint32_t iLinPtr,
259 VBOXHGCMLINPTR *paLinPtrs,
260 RTGCPHYS **ppPages)
261{
262 int rc = VINF_SUCCESS;
263
264 AssertRelease (u32Size > 0);
265
266 VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr];
267
268 /* Take the offset into the current page also into account! */
269 u32Size += GCPtr & PAGE_OFFSET_MASK;
270
271 uint32_t cPages = (u32Size + PAGE_SIZE - 1) / PAGE_SIZE;
272
273 Log(("vmmdevHGCMSaveLinPtr: parm %d: %RGv %d = %d pages\n", iParm, GCPtr, u32Size, cPages));
274
275 pLinPtr->iParm = iParm;
276 pLinPtr->offFirstPage = GCPtr & PAGE_OFFSET_MASK;
277 pLinPtr->cPages = cPages;
278 pLinPtr->paPages = *ppPages;
279
280 *ppPages += cPages;
281
282 uint32_t iPage = 0;
283
284 GCPtr &= PAGE_BASE_GC_MASK;
285
286 /* Gonvert the guest linear pointers of pages to HC addresses. */
287 while (iPage < cPages)
288 {
289 /* convert */
290 RTGCPHYS GCPhys;
291
292 rc = PDMDevHlpPhysGCPtr2GCPhys(pDevIns, GCPtr, &GCPhys);
293
294 Log(("vmmdevHGCMSaveLinPtr: Page %d: %RGv -> %RGp. %Rrc\n", iPage, GCPtr, GCPhys, rc));
295
296 if (RT_FAILURE (rc))
297 {
298 break;
299 }
300
301 /* store */
302 pLinPtr->paPages[iPage++] = GCPhys;
303
304 /* next */
305 GCPtr += PAGE_SIZE;
306 }
307
308 AssertRelease (iPage == cPages);
309
310 return rc;
311}
312
313static int vmmdevHGCMWriteLinPtr (PPDMDEVINS pDevIns,
314 uint32_t iParm,
315 void *pvHost,
316 uint32_t u32Size,
317 uint32_t iLinPtr,
318 VBOXHGCMLINPTR *paLinPtrs)
319{
320 int rc = VINF_SUCCESS;
321
322 VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr];
323
324 AssertRelease (u32Size > 0 && iParm == (uint32_t)pLinPtr->iParm);
325
326 RTGCPHYS GCPhysDst = pLinPtr->paPages[0] + pLinPtr->offFirstPage;
327 uint8_t *pu8Src = (uint8_t *)pvHost;
328
329 Log(("vmmdevHGCMWriteLinPtr: parm %d: size %d, cPages = %d\n", iParm, u32Size, pLinPtr->cPages));
330
331 uint32_t iPage = 0;
332
333 while (iPage < pLinPtr->cPages)
334 {
335 /* copy */
336 uint32_t cbWrite = iPage == 0?
337 PAGE_SIZE - pLinPtr->offFirstPage:
338 PAGE_SIZE;
339
340 Log(("vmmdevHGCMWriteLinPtr: page %d: dst %RGp, src %p, cbWrite %d\n", iPage, GCPhysDst, pu8Src, cbWrite));
341
342 iPage++;
343
344 if (cbWrite >= u32Size)
345 {
346 PDMDevHlpPhysWrite(pDevIns, GCPhysDst, pu8Src, u32Size);
347 u32Size = 0;
348 break;
349 }
350
351 PDMDevHlpPhysWrite(pDevIns, GCPhysDst, pu8Src, cbWrite);
352
353 /* next */
354 u32Size -= cbWrite;
355 pu8Src += cbWrite;
356
357 GCPhysDst = pLinPtr->paPages[iPage];
358 }
359
360 AssertRelease (iPage == pLinPtr->cPages);
361 Assert(u32Size == 0);
362
363 return rc;
364}
365
366DECLINLINE(bool) vmmdevHGCMPageListIsContiguous(const HGCMPageListInfo *pPgLst)
367{
368 if (pPgLst->cPages == 1)
369 return true;
370 RTGCPHYS64 Phys = pPgLst->aPages[0] + PAGE_SIZE;
371 if (Phys != pPgLst->aPages[1])
372 return false;
373 if (pPgLst->cPages > 2)
374 {
375 uint32_t iPage = 2;
376 do
377 {
378 Phys += PAGE_SIZE;
379 if (Phys != pPgLst->aPages[iPage])
380 return false;
381 iPage++;
382 } while (iPage < pPgLst->cPages);
383 }
384 return true;
385}
386
387static int vmmdevHGCMPageListRead(PPDMDEVINSR3 pDevIns, void *pvDst, uint32_t cbDst, const HGCMPageListInfo *pPageListInfo)
388{
389 /*
390 * Try detect contiguous buffers.
391 */
392 /** @todo We need a flag for indicating this. */
393 if (vmmdevHGCMPageListIsContiguous(pPageListInfo))
394 return PDMDevHlpPhysRead(pDevIns, pPageListInfo->aPages[0] | pPageListInfo->offFirstPage, pvDst, cbDst);
395
396 /*
397 * Page by page fallback
398 */
399 int rc = VINF_SUCCESS;
400
401 uint8_t *pu8Dst = (uint8_t *)pvDst;
402 uint32_t offPage = pPageListInfo->offFirstPage;
403 size_t cbRemaining = (size_t)cbDst;
404
405 uint32_t iPage;
406
407 for (iPage = 0; iPage < pPageListInfo->cPages; iPage++)
408 {
409 if (cbRemaining == 0)
410 {
411 break;
412 }
413
414 size_t cbChunk = PAGE_SIZE - offPage;
415
416 if (cbChunk > cbRemaining)
417 {
418 cbChunk = cbRemaining;
419 }
420
421 rc = PDMDevHlpPhysRead(pDevIns,
422 pPageListInfo->aPages[iPage] + offPage,
423 pu8Dst, cbChunk);
424
425 AssertRCBreak(rc);
426
427 offPage = 0; /* A next page is read from 0 offset. */
428 cbRemaining -= cbChunk;
429 pu8Dst += cbChunk;
430 }
431
432 return rc;
433}
434
435static int vmmdevHGCMPageListWrite(PPDMDEVINSR3 pDevIns, const HGCMPageListInfo *pPageListInfo, const void *pvSrc, uint32_t cbSrc)
436{
437 int rc = VINF_SUCCESS;
438
439 uint8_t *pu8Src = (uint8_t *)pvSrc;
440 uint32_t offPage = pPageListInfo->offFirstPage;
441 size_t cbRemaining = (size_t)cbSrc;
442
443 uint32_t iPage;
444 for (iPage = 0; iPage < pPageListInfo->cPages; iPage++)
445 {
446 if (cbRemaining == 0)
447 {
448 break;
449 }
450
451 size_t cbChunk = PAGE_SIZE - offPage;
452
453 if (cbChunk > cbRemaining)
454 {
455 cbChunk = cbRemaining;
456 }
457
458 rc = PDMDevHlpPhysWrite(pDevIns,
459 pPageListInfo->aPages[iPage] + offPage,
460 pu8Src, cbChunk);
461
462 AssertRCBreak(rc);
463
464 offPage = 0; /* A next page is read from 0 offset. */
465 cbRemaining -= cbChunk;
466 pu8Src += cbChunk;
467 }
468
469 return rc;
470}
471
472static void vmmdevRestoreSavedCommand(VBOXHGCMCMD *pCmd, VBOXHGCMCMD *pSavedCmd)
473{
474 /* Copy relevant saved command information to the new allocated structure. */
475 pCmd->enmCmdType = pSavedCmd->enmCmdType;
476 pCmd->fCancelled = pSavedCmd->fCancelled;
477 pCmd->GCPhys = pSavedCmd->GCPhys;
478 pCmd->cbSize = pSavedCmd->cbSize;
479 pCmd->cLinPtrs = pSavedCmd->cLinPtrs;
480 pCmd->cLinPtrPages = pSavedCmd->cLinPtrPages;
481 pCmd->paLinPtrs = pSavedCmd->paLinPtrs;
482
483 /* The new allocated command owns the 'paLinPtrs' pointer. */
484 pSavedCmd->paLinPtrs = NULL;
485}
486
487int vmmdevHGCMConnect (PVMMDEV pThis, VMMDevHGCMConnect *pHGCMConnect, RTGCPHYS GCPhys)
488{
489 int rc = VINF_SUCCESS;
490
491 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + pHGCMConnect->header.header.size;
492
493 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
494
495 if (pCmd)
496 {
497 VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pCmd+1);
498
499 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMConnect->header.header.size, VBOXHGCMCMDTYPE_CONNECT);
500
501 memcpy(pHGCMConnectCopy, pHGCMConnect, pHGCMConnect->header.header.size);
502
503 pCmd->paHostParms = NULL;
504 pCmd->cLinPtrs = 0;
505 pCmd->paLinPtrs = NULL;
506
507 /* Only allow the guest to use existing services! */
508 Assert(pHGCMConnectCopy->loc.type == VMMDevHGCMLoc_LocalHost_Existing);
509 pHGCMConnectCopy->loc.type = VMMDevHGCMLoc_LocalHost_Existing;
510
511 rc = pThis->pHGCMDrv->pfnConnect (pThis->pHGCMDrv, pCmd, &pHGCMConnectCopy->loc, &pHGCMConnectCopy->u32ClientID);
512 }
513 else
514 {
515 rc = VERR_NO_MEMORY;
516 }
517
518 return rc;
519}
520
521static int vmmdevHGCMConnectSaved (PVMMDEV pThis, VMMDevHGCMConnect *pHGCMConnect, RTGCPHYS GCPhys, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd, VBOXHGCMCMD **ppCmd)
522{
523 int rc = VINF_SUCCESS;
524
525 /* Allocate buffer for the new command. */
526 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + pHGCMConnect->header.header.size;
527
528 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
529
530 if (pCmd)
531 {
532 vmmdevRestoreSavedCommand(pCmd, pSavedCmd);
533 *ppCmd = pCmd;
534
535 VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pCmd+1);
536
537 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMConnect->header.header.size, VBOXHGCMCMDTYPE_CONNECT);
538
539 memcpy(pHGCMConnectCopy, pHGCMConnect, pHGCMConnect->header.header.size);
540
541 /* Only allow the guest to use existing services! */
542 Assert(pHGCMConnectCopy->loc.type == VMMDevHGCMLoc_LocalHost_Existing);
543 pHGCMConnectCopy->loc.type = VMMDevHGCMLoc_LocalHost_Existing;
544
545 rc = pThis->pHGCMDrv->pfnConnect (pThis->pHGCMDrv, pCmd, &pHGCMConnectCopy->loc, &pHGCMConnectCopy->u32ClientID);
546
547 if (RT_SUCCESS (rc))
548 {
549 *pfHGCMCalled = true;
550 }
551 }
552 else
553 {
554 rc = VERR_NO_MEMORY;
555 }
556
557 return rc;
558}
559
560int vmmdevHGCMDisconnect (PVMMDEV pThis, VMMDevHGCMDisconnect *pHGCMDisconnect, RTGCPHYS GCPhys)
561{
562 int rc = VINF_SUCCESS;
563
564 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD);
565
566 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
567
568 if (pCmd)
569 {
570 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMDisconnect->header.header.size, VBOXHGCMCMDTYPE_DISCONNECT);
571
572 pCmd->paHostParms = NULL;
573 pCmd->cLinPtrs = 0;
574 pCmd->paLinPtrs = NULL;
575
576 rc = pThis->pHGCMDrv->pfnDisconnect (pThis->pHGCMDrv, pCmd, pHGCMDisconnect->u32ClientID);
577 }
578 else
579 {
580 rc = VERR_NO_MEMORY;
581 }
582
583 return rc;
584}
585
586static int vmmdevHGCMDisconnectSaved (PVMMDEV pThis, VMMDevHGCMDisconnect *pHGCMDisconnect, RTGCPHYS GCPhys, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd, VBOXHGCMCMD **ppCmd)
587{
588 int rc = VINF_SUCCESS;
589
590 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD);
591
592 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
593
594 if (pCmd)
595 {
596 vmmdevRestoreSavedCommand(pCmd, pSavedCmd);
597 *ppCmd = pCmd;
598
599 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMDisconnect->header.header.size, VBOXHGCMCMDTYPE_DISCONNECT);
600
601 pCmd->paHostParms = NULL;
602 pCmd->cLinPtrs = 0;
603 pCmd->paLinPtrs = NULL;
604
605 rc = pThis->pHGCMDrv->pfnDisconnect (pThis->pHGCMDrv, pCmd, pHGCMDisconnect->u32ClientID);
606
607 if (RT_SUCCESS (rc))
608 {
609 *pfHGCMCalled = true;
610 }
611 }
612 else
613 {
614 rc = VERR_NO_MEMORY;
615 }
616
617 return rc;
618}
619
620int vmmdevHGCMCall (PVMMDEV pThis, VMMDevHGCMCall *pHGCMCall, uint32_t cbHGCMCall, RTGCPHYS GCPhys, bool f64Bits)
621{
622 int rc = VINF_SUCCESS;
623
624 Log(("vmmdevHGCMCall: client id = %d, function = %d, %s bit\n", pHGCMCall->u32ClientID, pHGCMCall->u32Function, f64Bits? "64": "32"));
625
626 /* Compute size and allocate memory block to hold:
627 * struct VBOXHGCMCMD
628 * VBOXHGCMSVCPARM[cParms]
629 * memory buffers for pointer parameters.
630 */
631
632 uint32_t cParms = pHGCMCall->cParms;
633
634 Log(("vmmdevHGCMCall: cParms = %d\n", cParms));
635
636 /*
637 * Compute size of required memory buffer.
638 */
639
640 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + cParms * sizeof (VBOXHGCMSVCPARM);
641
642 uint32_t i;
643
644 uint32_t cLinPtrs = 0;
645 uint32_t cLinPtrPages = 0;
646
647 if (f64Bits)
648 {
649#ifdef VBOX_WITH_64_BITS_GUESTS
650 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
651#else
652 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
653 AssertFailed (); /* This code should not be called in this case */
654#endif /* VBOX_WITH_64_BITS_GUESTS */
655
656 /* Look for pointer parameters, which require a host buffer. */
657 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
658 {
659 switch (pGuestParm->type)
660 {
661 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
662 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
663 case VMMDevHGCMParmType_LinAddr: /* In & Out */
664 {
665 if (pGuestParm->u.Pointer.size > 0)
666 {
667 /* Only pointers with some actual data are counted. */
668 cbCmdSize += pGuestParm->u.Pointer.size;
669
670 cLinPtrs++;
671 /* Take the offset into the current page also into account! */
672 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
673 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
674 }
675
676 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
677 } break;
678
679 case VMMDevHGCMParmType_PageList:
680 {
681 cbCmdSize += pGuestParm->u.PageList.size;
682 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
683 } break;
684
685 case VMMDevHGCMParmType_32bit:
686 case VMMDevHGCMParmType_64bit:
687 {
688 } break;
689
690 default:
691 case VMMDevHGCMParmType_PhysAddr:
692 {
693 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
694 rc = VERR_INVALID_PARAMETER;
695 break;
696 }
697 }
698 }
699 }
700 else
701 {
702#ifdef VBOX_WITH_64_BITS_GUESTS
703 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
704#else
705 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
706#endif /* VBOX_WITH_64_BITS_GUESTS */
707
708 /* Look for pointer parameters, which require a host buffer. */
709 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
710 {
711 switch (pGuestParm->type)
712 {
713 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
714 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
715 case VMMDevHGCMParmType_LinAddr: /* In & Out */
716 {
717 if (pGuestParm->u.Pointer.size > 0)
718 {
719 /* Only pointers with some actual data are counted. */
720 cbCmdSize += pGuestParm->u.Pointer.size;
721
722 cLinPtrs++;
723 /* Take the offset into the current page also into account! */
724 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
725 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
726 }
727
728 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
729 } break;
730
731 case VMMDevHGCMParmType_PageList:
732 {
733 cbCmdSize += pGuestParm->u.PageList.size;
734 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
735 } break;
736
737 case VMMDevHGCMParmType_32bit:
738 case VMMDevHGCMParmType_64bit:
739 {
740 } break;
741
742 default:
743 {
744 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
745 rc = VERR_INVALID_PARAMETER;
746 break;
747 }
748 }
749 }
750 }
751
752 if (RT_FAILURE (rc))
753 {
754 return rc;
755 }
756
757 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAlloc (cbCmdSize);
758
759 if (pCmd == NULL)
760 {
761 return VERR_NO_MEMORY;
762 }
763
764 memset (pCmd, 0, sizeof (*pCmd));
765
766 pCmd->paHostParms = NULL;
767 pCmd->cLinPtrs = cLinPtrs;
768 pCmd->cLinPtrPages = cLinPtrPages;
769
770 if (cLinPtrs > 0)
771 {
772 pCmd->paLinPtrs = (VBOXHGCMLINPTR *)RTMemAlloc ( sizeof (VBOXHGCMLINPTR) * cLinPtrs
773 + sizeof (RTGCPHYS) * cLinPtrPages);
774
775 if (pCmd->paLinPtrs == NULL)
776 {
777 RTMemFree (pCmd);
778 return VERR_NO_MEMORY;
779 }
780 }
781 else
782 {
783 pCmd->paLinPtrs = NULL;
784 }
785
786 VBOXDD_HGCMCALL_ENTER(pCmd, pHGCMCall->u32Function, pHGCMCall->u32ClientID, cbCmdSize);
787
788 /* Process parameters, changing them to host context pointers for easy
789 * processing by connector. Guest must insure that the pointed data is actually
790 * in the guest RAM and remains locked there for entire request processing.
791 */
792
793 if (cParms != 0)
794 {
795 /* Compute addresses of host parms array and first memory buffer. */
796 VBOXHGCMSVCPARM *pHostParm = (VBOXHGCMSVCPARM *)((char *)pCmd + sizeof (struct VBOXHGCMCMD));
797
798 uint8_t *pcBuf = (uint8_t *)pHostParm + cParms * sizeof (VBOXHGCMSVCPARM);
799
800 pCmd->paHostParms = pHostParm;
801
802 uint32_t iLinPtr = 0;
803 RTGCPHYS *pPages = (RTGCPHYS *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) *cLinPtrs);
804
805 if (f64Bits)
806 {
807#ifdef VBOX_WITH_64_BITS_GUESTS
808 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
809#else
810 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
811 AssertFailed (); /* This code should not be called in this case */
812#endif /* VBOX_WITH_64_BITS_GUESTS */
813
814
815 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
816 {
817 switch (pGuestParm->type)
818 {
819 case VMMDevHGCMParmType_32bit:
820 {
821 uint32_t u32 = pGuestParm->u.value32;
822
823 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
824 pHostParm->u.uint32 = u32;
825
826 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
827 break;
828 }
829
830 case VMMDevHGCMParmType_64bit:
831 {
832 uint64_t u64 = pGuestParm->u.value64;
833
834 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
835 pHostParm->u.uint64 = u64;
836
837 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
838 break;
839 }
840
841 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
842 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
843 case VMMDevHGCMParmType_LinAddr: /* In & Out */
844 {
845 uint32_t size = pGuestParm->u.Pointer.size;
846 RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr;
847
848 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
849 pHostParm->u.pointer.size = size;
850
851 /* Copy guest data to an allocated buffer, so
852 * services can use the data.
853 */
854
855 if (size == 0)
856 {
857 pHostParm->u.pointer.addr = NULL;
858 }
859 else
860 {
861 /* Don't overdo it */
862 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
863 rc = PDMDevHlpPhysReadGCVirt(pThis->pDevIns, pcBuf, linearAddr, size);
864 else
865 rc = VINF_SUCCESS;
866
867 if (RT_SUCCESS(rc))
868 {
869 pHostParm->u.pointer.addr = pcBuf;
870 pcBuf += size;
871
872 /* Remember the guest physical pages that belong to the virtual address region.
873 * Do it for all linear pointers because load state will require In pointer info too.
874 */
875 rc = vmmdevHGCMSaveLinPtr (pThis->pDevIns, i, linearAddr, size, iLinPtr, pCmd->paLinPtrs, &pPages);
876
877 iLinPtr++;
878 }
879 }
880
881 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", linearAddr, rc));
882 break;
883 }
884
885 case VMMDevHGCMParmType_PageList:
886 {
887 uint32_t size = pGuestParm->u.PageList.size;
888
889 /* Check that the page list info is within the request. */
890 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
891 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
892 {
893 rc = VERR_INVALID_PARAMETER;
894 break;
895 }
896
897 /* At least the structure is within. */
898 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
899
900 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
901
902 if ( pPageListInfo->cPages == 0
903 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
904 {
905 rc = VERR_INVALID_PARAMETER;
906 break;
907 }
908
909 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
910 pHostParm->u.pointer.size = size;
911
912 /* Copy guest data to an allocated buffer, so
913 * services can use the data.
914 */
915
916 if (size == 0)
917 {
918 pHostParm->u.pointer.addr = NULL;
919 }
920 else
921 {
922 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
923 {
924 /* Copy pages to the pcBuf[size]. */
925 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pcBuf, size, pPageListInfo);
926 }
927 else
928 rc = VINF_SUCCESS;
929
930 if (RT_SUCCESS(rc))
931 {
932 pHostParm->u.pointer.addr = pcBuf;
933 pcBuf += size;
934 }
935 }
936
937 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
938 break;
939 }
940
941 /* just to shut up gcc */
942 default:
943 AssertFailed();
944 break;
945 }
946 }
947 }
948 else
949 {
950#ifdef VBOX_WITH_64_BITS_GUESTS
951 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
952#else
953 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
954#endif /* VBOX_WITH_64_BITS_GUESTS */
955
956 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
957 {
958 switch (pGuestParm->type)
959 {
960 case VMMDevHGCMParmType_32bit:
961 {
962 uint32_t u32 = pGuestParm->u.value32;
963
964 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
965 pHostParm->u.uint32 = u32;
966
967 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
968 break;
969 }
970
971 case VMMDevHGCMParmType_64bit:
972 {
973 uint64_t u64 = pGuestParm->u.value64;
974
975 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
976 pHostParm->u.uint64 = u64;
977
978 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
979 break;
980 }
981
982 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
983 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
984 case VMMDevHGCMParmType_LinAddr: /* In & Out */
985 {
986 uint32_t size = pGuestParm->u.Pointer.size;
987 RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr;
988
989 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
990 pHostParm->u.pointer.size = size;
991
992 /* Copy guest data to an allocated buffer, so
993 * services can use the data.
994 */
995
996 if (size == 0)
997 {
998 pHostParm->u.pointer.addr = NULL;
999 }
1000 else
1001 {
1002 /* Don't overdo it */
1003 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
1004 rc = PDMDevHlpPhysReadGCVirt(pThis->pDevIns, pcBuf, linearAddr, size);
1005 else
1006 rc = VINF_SUCCESS;
1007
1008 if (RT_SUCCESS(rc))
1009 {
1010 pHostParm->u.pointer.addr = pcBuf;
1011 pcBuf += size;
1012
1013 /* Remember the guest physical pages that belong to the virtual address region.
1014 * Do it for all linear pointers because load state will require In pointer info too.
1015 */
1016 rc = vmmdevHGCMSaveLinPtr (pThis->pDevIns, i, linearAddr, size, iLinPtr, pCmd->paLinPtrs, &pPages);
1017
1018 iLinPtr++;
1019 }
1020 }
1021
1022 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", linearAddr, rc));
1023 break;
1024 }
1025
1026 case VMMDevHGCMParmType_PageList:
1027 {
1028 uint32_t size = pGuestParm->u.PageList.size;
1029
1030 /* Check that the page list info is within the request. */
1031 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
1032 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
1033 {
1034 rc = VERR_INVALID_PARAMETER;
1035 break;
1036 }
1037
1038 /* At least the structure is within. */
1039 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
1040
1041 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
1042
1043 if ( pPageListInfo->cPages == 0
1044 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
1045 {
1046 rc = VERR_INVALID_PARAMETER;
1047 break;
1048 }
1049
1050 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1051 pHostParm->u.pointer.size = size;
1052
1053 /* Copy guest data to an allocated buffer, so
1054 * services can use the data.
1055 */
1056
1057 if (size == 0)
1058 {
1059 pHostParm->u.pointer.addr = NULL;
1060 }
1061 else
1062 {
1063 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
1064 {
1065 /* Copy pages to the pcBuf[size]. */
1066 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pcBuf, size, pPageListInfo);
1067 }
1068 else
1069 rc = VINF_SUCCESS;
1070
1071 if (RT_SUCCESS(rc))
1072 {
1073 pHostParm->u.pointer.addr = pcBuf;
1074 pcBuf += size;
1075 }
1076 }
1077
1078 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
1079 break;
1080 }
1081
1082 /* just to shut up gcc */
1083 default:
1084 AssertFailed();
1085 break;
1086 }
1087 }
1088 }
1089 }
1090
1091 if (RT_SUCCESS (rc))
1092 {
1093 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMCall->header.header.size, VBOXHGCMCMDTYPE_CALL);
1094
1095 /* Pass the function call to HGCM connector for actual processing */
1096 rc = pThis->pHGCMDrv->pfnCall (pThis->pHGCMDrv, pCmd, pHGCMCall->u32ClientID,
1097 pHGCMCall->u32Function, cParms, pCmd->paHostParms);
1098
1099 if (RT_FAILURE (rc))
1100 {
1101 Log(("vmmdevHGCMCall: pfnCall failed rc = %Rrc\n", rc));
1102 vmmdevHGCMRemoveCommand (pThis, pCmd);
1103 }
1104 }
1105
1106 if (RT_FAILURE (rc))
1107 {
1108 if (pCmd->paLinPtrs)
1109 {
1110 RTMemFree (pCmd->paLinPtrs);
1111 }
1112
1113 RTMemFree (pCmd);
1114 }
1115
1116 return rc;
1117}
1118
1119static void logRelLoadStatePointerIndexMismatch (uint32_t iParm, uint32_t iSavedParm, int iLinPtr, int cLinPtrs)
1120{
1121 LogRel(("Warning: VMMDev load state: a pointer parameter index mismatch %d (expected %d) (%d/%d)\n",
1122 (int)iParm, (int)iSavedParm, iLinPtr, cLinPtrs));
1123}
1124
1125static void logRelLoadStateBufferSizeMismatch (uint32_t size, uint32_t iPage, uint32_t cPages)
1126{
1127 LogRel(("Warning: VMMDev load state: buffer size mismatch: size %d, page %d/%d\n",
1128 (int)size, (int)iPage, (int)cPages));
1129}
1130
1131
1132static int vmmdevHGCMCallSaved (PVMMDEV pThis, VMMDevHGCMCall *pHGCMCall, RTGCPHYS GCPhys, uint32_t cbHGCMCall, bool f64Bits, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd, VBOXHGCMCMD **ppCmd)
1133{
1134 int rc = VINF_SUCCESS;
1135
1136 Log(("vmmdevHGCMCallSaved: client id = %d, function = %d, %s bit\n", pHGCMCall->u32ClientID, pHGCMCall->u32Function, f64Bits? "64": "32"));
1137
1138 /* Compute size and allocate memory block to hold:
1139 * struct VBOXHGCMCMD
1140 * VBOXHGCMSVCPARM[cParms]
1141 * memory buffers for pointer parameters.
1142 */
1143
1144 uint32_t cParms = pHGCMCall->cParms;
1145
1146 Log(("vmmdevHGCMCall: cParms = %d\n", cParms));
1147
1148 /*
1149 * Compute size of required memory buffer.
1150 */
1151
1152 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + cParms * sizeof (VBOXHGCMSVCPARM);
1153
1154 uint32_t i;
1155
1156 int32_t cLinPtrs = 0;
1157 int32_t cLinPtrPages = 0;
1158
1159 if (f64Bits)
1160 {
1161#ifdef VBOX_WITH_64_BITS_GUESTS
1162 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
1163#else
1164 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1165 AssertFailed (); /* This code should not be called in this case */
1166#endif /* VBOX_WITH_64_BITS_GUESTS */
1167
1168 /* Look for pointer parameters, which require a host buffer. */
1169 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
1170 {
1171 switch (pGuestParm->type)
1172 {
1173 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1174 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1175 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1176 {
1177 if (pGuestParm->u.Pointer.size > 0)
1178 {
1179 /* Only pointers with some actual data are counted. */
1180 cbCmdSize += pGuestParm->u.Pointer.size;
1181
1182 cLinPtrs++;
1183 /* Take the offset into the current page also into account! */
1184 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
1185 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
1186 }
1187
1188 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
1189 } break;
1190
1191 case VMMDevHGCMParmType_PageList:
1192 {
1193 cbCmdSize += pGuestParm->u.PageList.size;
1194 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
1195 } break;
1196
1197 case VMMDevHGCMParmType_32bit:
1198 case VMMDevHGCMParmType_64bit:
1199 {
1200 } break;
1201
1202 default:
1203 case VMMDevHGCMParmType_PhysAddr:
1204 {
1205 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
1206 rc = VERR_INVALID_PARAMETER;
1207 break;
1208 }
1209 }
1210 }
1211 }
1212 else
1213 {
1214#ifdef VBOX_WITH_64_BITS_GUESTS
1215 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
1216#else
1217 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1218#endif /* VBOX_WITH_64_BITS_GUESTS */
1219
1220 /* Look for pointer parameters, which require a host buffer. */
1221 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
1222 {
1223 switch (pGuestParm->type)
1224 {
1225 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1226 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1227 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1228 {
1229 if (pGuestParm->u.Pointer.size > 0)
1230 {
1231 /* Only pointers with some actual data are counted. */
1232 cbCmdSize += pGuestParm->u.Pointer.size;
1233
1234 cLinPtrs++;
1235 /* Take the offset into the current page also into account! */
1236 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
1237 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
1238 }
1239
1240 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
1241 } break;
1242
1243 case VMMDevHGCMParmType_PageList:
1244 {
1245 cbCmdSize += pGuestParm->u.PageList.size;
1246 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
1247 } break;
1248
1249 case VMMDevHGCMParmType_32bit:
1250 case VMMDevHGCMParmType_64bit:
1251 {
1252 } break;
1253
1254 default:
1255 {
1256 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
1257 rc = VERR_INVALID_PARAMETER;
1258 break;
1259 }
1260 }
1261 }
1262 }
1263
1264 if (RT_FAILURE (rc))
1265 {
1266 return rc;
1267 }
1268
1269 if ( pSavedCmd->cLinPtrs != cLinPtrs
1270 || pSavedCmd->cLinPtrPages != cLinPtrPages)
1271 {
1272 LogRel(("VMMDev: invalid saved command ptrs: %d/%d, pages %d/%d\n",
1273 pSavedCmd->cLinPtrs, cLinPtrs, pSavedCmd->cLinPtrPages, cLinPtrPages));
1274 AssertFailed();
1275 return VERR_INVALID_PARAMETER;
1276 }
1277
1278 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
1279
1280 if (pCmd == NULL)
1281 {
1282 return VERR_NO_MEMORY;
1283 }
1284
1285 vmmdevRestoreSavedCommand(pCmd, pSavedCmd);
1286 *ppCmd = pCmd;
1287
1288 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMCall->header.header.size, VBOXHGCMCMDTYPE_CALL);
1289
1290 /* Process parameters, changing them to host context pointers for easy
1291 * processing by connector. Guest must insure that the pointed data is actually
1292 * in the guest RAM and remains locked there for entire request processing.
1293 */
1294
1295 if (cParms != 0)
1296 {
1297 /* Compute addresses of host parms array and first memory buffer. */
1298 VBOXHGCMSVCPARM *pHostParm = (VBOXHGCMSVCPARM *)((uint8_t *)pCmd + sizeof (struct VBOXHGCMCMD));
1299
1300 uint8_t *pu8Buf = (uint8_t *)pHostParm + cParms * sizeof (VBOXHGCMSVCPARM);
1301
1302 pCmd->paHostParms = pHostParm;
1303
1304 uint32_t iParm;
1305 int iLinPtr = 0;
1306
1307 if (f64Bits)
1308 {
1309#ifdef VBOX_WITH_64_BITS_GUESTS
1310 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
1311#else
1312 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1313 AssertFailed (); /* This code should not be called in this case */
1314#endif /* VBOX_WITH_64_BITS_GUESTS */
1315
1316 for (iParm = 0; iParm < cParms && RT_SUCCESS(rc); iParm++, pGuestParm++, pHostParm++)
1317 {
1318 switch (pGuestParm->type)
1319 {
1320 case VMMDevHGCMParmType_32bit:
1321 {
1322 uint32_t u32 = pGuestParm->u.value32;
1323
1324 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
1325 pHostParm->u.uint32 = u32;
1326
1327 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
1328 break;
1329 }
1330
1331 case VMMDevHGCMParmType_64bit:
1332 {
1333 uint64_t u64 = pGuestParm->u.value64;
1334
1335 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
1336 pHostParm->u.uint64 = u64;
1337
1338 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
1339 break;
1340 }
1341
1342 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1343 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1344 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1345 {
1346 uint32_t size = pGuestParm->u.Pointer.size;
1347
1348 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1349 pHostParm->u.pointer.size = size;
1350
1351 /* Copy guest data to an allocated buffer, so
1352 * services can use the data.
1353 */
1354
1355 if (size == 0)
1356 {
1357 pHostParm->u.pointer.addr = NULL;
1358 }
1359 else
1360 {
1361 /* The saved command already have the page list in pCmd->paLinPtrs.
1362 * Read data from guest pages.
1363 */
1364 /* Don't overdo it */
1365 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
1366 {
1367 if ( iLinPtr >= pCmd->cLinPtrs
1368 || pCmd->paLinPtrs[iLinPtr].iParm != iParm)
1369 {
1370 logRelLoadStatePointerIndexMismatch (iParm, pCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pCmd->cLinPtrs);
1371 rc = VERR_INVALID_PARAMETER;
1372 }
1373 else
1374 {
1375 VBOXHGCMLINPTR *pLinPtr = &pCmd->paLinPtrs[iLinPtr];
1376
1377 uint32_t iPage;
1378 uint32_t offPage = pLinPtr->offFirstPage;
1379 size_t cbRemaining = size;
1380 uint8_t *pu8Dst = pu8Buf;
1381 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
1382 {
1383 if (cbRemaining == 0)
1384 {
1385 logRelLoadStateBufferSizeMismatch (size, iPage, pLinPtr->cPages);
1386 break;
1387 }
1388
1389 size_t cbChunk = PAGE_SIZE - offPage;
1390
1391 if (cbChunk > cbRemaining)
1392 {
1393 cbChunk = cbRemaining;
1394 }
1395
1396 rc = PDMDevHlpPhysRead(pThis->pDevIns,
1397 pLinPtr->paPages[iPage] + offPage,
1398 pu8Dst, cbChunk);
1399
1400 AssertRCBreak(rc);
1401
1402 offPage = 0; /* A next page is read from 0 offset. */
1403 cbRemaining -= cbChunk;
1404 pu8Dst += cbChunk;
1405 }
1406 }
1407 }
1408 else
1409 rc = VINF_SUCCESS;
1410
1411 if (RT_SUCCESS(rc))
1412 {
1413 pHostParm->u.pointer.addr = pu8Buf;
1414 pu8Buf += size;
1415
1416 iLinPtr++;
1417 }
1418 }
1419
1420 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n",
1421 pGuestParm->u.Pointer.u.linearAddr, rc));
1422 break;
1423 }
1424
1425 case VMMDevHGCMParmType_PageList:
1426 {
1427 uint32_t size = pGuestParm->u.PageList.size;
1428
1429 /* Check that the page list info is within the request. */
1430 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
1431 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
1432 {
1433 rc = VERR_INVALID_PARAMETER;
1434 break;
1435 }
1436
1437 /* At least the structure is within. */
1438 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
1439
1440 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
1441
1442 if ( pPageListInfo->cPages == 0
1443 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
1444 {
1445 rc = VERR_INVALID_PARAMETER;
1446 break;
1447 }
1448
1449 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1450 pHostParm->u.pointer.size = size;
1451
1452 /* Copy guest data to an allocated buffer, so
1453 * services can use the data.
1454 */
1455
1456 if (size == 0)
1457 {
1458 pHostParm->u.pointer.addr = NULL;
1459 }
1460 else
1461 {
1462 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
1463 {
1464 /* Copy pages to the pcBuf[size]. */
1465 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pu8Buf, size, pPageListInfo);
1466 }
1467 else
1468 rc = VINF_SUCCESS;
1469
1470 if (RT_SUCCESS(rc))
1471 {
1472 pHostParm->u.pointer.addr = pu8Buf;
1473 pu8Buf += size;
1474 }
1475 }
1476
1477 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
1478 break;
1479 }
1480
1481 /* just to shut up gcc */
1482 default:
1483 AssertFailed();
1484 break;
1485 }
1486 }
1487 }
1488 else
1489 {
1490#ifdef VBOX_WITH_64_BITS_GUESTS
1491 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
1492#else
1493 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1494#endif /* VBOX_WITH_64_BITS_GUESTS */
1495
1496 for (iParm = 0; iParm < cParms && RT_SUCCESS(rc); iParm++, pGuestParm++, pHostParm++)
1497 {
1498 switch (pGuestParm->type)
1499 {
1500 case VMMDevHGCMParmType_32bit:
1501 {
1502 uint32_t u32 = pGuestParm->u.value32;
1503
1504 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
1505 pHostParm->u.uint32 = u32;
1506
1507 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
1508 break;
1509 }
1510
1511 case VMMDevHGCMParmType_64bit:
1512 {
1513 uint64_t u64 = pGuestParm->u.value64;
1514
1515 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
1516 pHostParm->u.uint64 = u64;
1517
1518 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
1519 break;
1520 }
1521
1522 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1523 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1524 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1525 {
1526 uint32_t size = pGuestParm->u.Pointer.size;
1527
1528 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1529 pHostParm->u.pointer.size = size;
1530
1531 /* Copy guest data to an allocated buffer, so
1532 * services can use the data.
1533 */
1534
1535 if (size == 0)
1536 {
1537 pHostParm->u.pointer.addr = NULL;
1538 }
1539 else
1540 {
1541 /* The saved command already have the page list in pCmd->paLinPtrs.
1542 * Read data from guest pages.
1543 */
1544 /* Don't overdo it */
1545 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
1546 {
1547 if ( iLinPtr >= pCmd->cLinPtrs
1548 || pCmd->paLinPtrs[iLinPtr].iParm != iParm)
1549 {
1550 logRelLoadStatePointerIndexMismatch (iParm, pCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pCmd->cLinPtrs);
1551 rc = VERR_INVALID_PARAMETER;
1552 }
1553 else
1554 {
1555 VBOXHGCMLINPTR *pLinPtr = &pCmd->paLinPtrs[iLinPtr];
1556
1557 uint32_t iPage;
1558 uint32_t offPage = pLinPtr->offFirstPage;
1559 size_t cbRemaining = size;
1560 uint8_t *pu8Dst = pu8Buf;
1561 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
1562 {
1563 if (cbRemaining == 0)
1564 {
1565 logRelLoadStateBufferSizeMismatch (size, iPage, pLinPtr->cPages);
1566 break;
1567 }
1568
1569 size_t cbChunk = PAGE_SIZE - offPage;
1570
1571 if (cbChunk > cbRemaining)
1572 {
1573 cbChunk = cbRemaining;
1574 }
1575
1576 rc = PDMDevHlpPhysRead(pThis->pDevIns,
1577 pLinPtr->paPages[iPage] + offPage,
1578 pu8Dst, cbChunk);
1579
1580 AssertRCBreak(rc);
1581
1582 offPage = 0; /* A next page is read from 0 offset. */
1583 cbRemaining -= cbChunk;
1584 pu8Dst += cbChunk;
1585 }
1586 }
1587 }
1588 else
1589 rc = VINF_SUCCESS;
1590
1591 if (RT_SUCCESS(rc))
1592 {
1593 pHostParm->u.pointer.addr = pu8Buf;
1594 pu8Buf += size;
1595
1596 iLinPtr++;
1597 }
1598 }
1599
1600 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n",
1601 pGuestParm->u.Pointer.u.linearAddr, rc));
1602 break;
1603 }
1604
1605 case VMMDevHGCMParmType_PageList:
1606 {
1607 uint32_t size = pGuestParm->u.PageList.size;
1608
1609 /* Check that the page list info is within the request. */
1610 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
1611 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
1612 {
1613 rc = VERR_INVALID_PARAMETER;
1614 break;
1615 }
1616
1617 /* At least the structure is within. */
1618 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
1619
1620 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
1621
1622 if ( pPageListInfo->cPages == 0
1623 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
1624 {
1625 rc = VERR_INVALID_PARAMETER;
1626 break;
1627 }
1628
1629 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1630 pHostParm->u.pointer.size = size;
1631
1632 /* Copy guest data to an allocated buffer, so
1633 * services can use the data.
1634 */
1635
1636 if (size == 0)
1637 {
1638 pHostParm->u.pointer.addr = NULL;
1639 }
1640 else
1641 {
1642 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
1643 {
1644 /* Copy pages to the pcBuf[size]. */
1645 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pu8Buf, size, pPageListInfo);
1646 }
1647 else
1648 rc = VINF_SUCCESS;
1649
1650 if (RT_SUCCESS(rc))
1651 {
1652 pHostParm->u.pointer.addr = pu8Buf;
1653 pu8Buf += size;
1654 }
1655 }
1656
1657 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
1658 break;
1659 }
1660
1661 /* just to shut up gcc */
1662 default:
1663 AssertFailed();
1664 break;
1665 }
1666 }
1667 }
1668 }
1669
1670 if (RT_SUCCESS (rc))
1671 {
1672 /* Pass the function call to HGCM connector for actual processing */
1673 rc = pThis->pHGCMDrv->pfnCall (pThis->pHGCMDrv, pCmd, pHGCMCall->u32ClientID, pHGCMCall->u32Function, cParms, pCmd->paHostParms);
1674 if (RT_SUCCESS (rc))
1675 {
1676 *pfHGCMCalled = true;
1677 }
1678 }
1679
1680 return rc;
1681}
1682
1683/**
1684 * VMMDevReq_HGCMCancel worker.
1685 *
1686 * @thread EMT
1687 */
1688int vmmdevHGCMCancel (PVMMDEV pThis, VMMDevHGCMCancel *pHGCMCancel, RTGCPHYS GCPhys)
1689{
1690 NOREF(pHGCMCancel);
1691 int rc = vmmdevHGCMCancel2(pThis, GCPhys);
1692 return rc == VERR_NOT_FOUND ? VERR_INVALID_PARAMETER : rc;
1693}
1694
1695/**
1696 * VMMDevReq_HGCMCancel2 worker.
1697 *
1698 * @retval VINF_SUCCESS on success.
1699 * @retval VERR_NOT_FOUND if the request was not found.
1700 * @retval VERR_INVALID_PARAMETER if the request address is invalid.
1701 *
1702 * @param pThis The VMMDev instance data.
1703 * @param GCPhys The address of the request that should be cancelled.
1704 *
1705 * @thread EMT
1706 */
1707int vmmdevHGCMCancel2 (PVMMDEV pThis, RTGCPHYS GCPhys)
1708{
1709 if ( GCPhys == 0
1710 || GCPhys == NIL_RTGCPHYS
1711 || GCPhys == NIL_RTGCPHYS32)
1712 {
1713 Log(("vmmdevHGCMCancel2: GCPhys=%#x\n", GCPhys));
1714 return VERR_INVALID_PARAMETER;
1715 }
1716
1717 /*
1718 * Locate the command and cancel it while under the protection of
1719 * the lock. hgcmCompletedWorker makes assumptions about this.
1720 */
1721 int rc = vmmdevHGCMCmdListLock (pThis);
1722 AssertRCReturn(rc, rc);
1723
1724 PVBOXHGCMCMD pCmd = vmmdevHGCMFindCommandLocked (pThis, GCPhys);
1725 if (pCmd)
1726 {
1727 pCmd->fCancelled = true;
1728 Log(("vmmdevHGCMCancel2: Cancelled pCmd=%p / GCPhys=%#x\n", pCmd, GCPhys));
1729 }
1730 else
1731 rc = VERR_NOT_FOUND;
1732
1733 vmmdevHGCMCmdListUnlock (pThis);
1734 return rc;
1735}
1736
1737static int vmmdevHGCMCmdVerify (PVBOXHGCMCMD pCmd, VMMDevHGCMRequestHeader *pHeader)
1738{
1739 switch (pCmd->enmCmdType)
1740 {
1741 case VBOXHGCMCMDTYPE_CONNECT:
1742 if ( pHeader->header.requestType == VMMDevReq_HGCMConnect
1743 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1744 break;
1745
1746 case VBOXHGCMCMDTYPE_DISCONNECT:
1747 if ( pHeader->header.requestType == VMMDevReq_HGCMDisconnect
1748 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1749 break;
1750
1751 case VBOXHGCMCMDTYPE_CALL:
1752#ifdef VBOX_WITH_64_BITS_GUESTS
1753 if ( pHeader->header.requestType == VMMDevReq_HGCMCall32
1754 || pHeader->header.requestType == VMMDevReq_HGCMCall64
1755 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1756#else
1757 if ( pHeader->header.requestType == VMMDevReq_HGCMCall
1758 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1759#endif /* VBOX_WITH_64_BITS_GUESTS */
1760
1761 break;
1762
1763 default:
1764 AssertFailed ();
1765 }
1766
1767 LogRel(("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n",
1768 pCmd->enmCmdType, pHeader->header.requestType));
1769 return VERR_INVALID_PARAMETER;
1770}
1771
1772DECLCALLBACK(void) hgcmCompletedWorker (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
1773{
1774 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1775#ifdef VBOX_WITH_DTRACE
1776 uint32_t idFunction = 0;
1777 uint32_t idClient = 0;
1778#endif
1779
1780 int rc = VINF_SUCCESS;
1781
1782 if (result == VINF_HGCM_SAVE_STATE)
1783 {
1784 /* If the completion routine was called because HGCM saves its state,
1785 * then currently nothing to be done here. The pCmd stays in the list
1786 * and will be saved later when the VMMDev state will be saved.
1787 *
1788 * It it assumed that VMMDev saves state after the HGCM services,
1789 * and, therefore, VBOXHGCMCMD structures are not removed by
1790 * vmmdevHGCMSaveState from the list, while HGCM uses them.
1791 */
1792 LogFlowFunc(("VINF_HGCM_SAVE_STATE for command %p\n", pCmd));
1793 return;
1794 }
1795
1796 /*
1797 * The cancellation protocol requires us to remove the command here
1798 * and then check the flag. Cancelled commands must not be written
1799 * back to guest memory.
1800 */
1801 VBOXDD_HGCMCALL_COMPLETED_EMT(pCmd, result);
1802 vmmdevHGCMRemoveCommand (pThis, pCmd);
1803
1804 if (pCmd->fCancelled || pCmd->fSaved)
1805 {
1806 LogFlowFunc(("A cancelled command %p: %d %d\n", pCmd, pCmd->fCancelled, pCmd->fSaved));
1807 }
1808 else
1809 {
1810 /* Preallocated block for requests which have up to 8 parameters (most of requests). */
1811#ifdef VBOX_WITH_64_BITS_GUESTS
1812 uint8_t au8Prealloc[sizeof (VMMDevHGCMCall) + 8 * sizeof (HGCMFunctionParameter64)];
1813#else
1814 uint8_t au8Prealloc[sizeof (VMMDevHGCMCall) + 8 * sizeof (HGCMFunctionParameter)];
1815#endif /* VBOX_WITH_64_BITS_GUESTS */
1816
1817 VMMDevHGCMRequestHeader *pHeader;
1818
1819 if (pCmd->cbSize <= sizeof (au8Prealloc))
1820 {
1821 pHeader = (VMMDevHGCMRequestHeader *)&au8Prealloc[0];
1822 }
1823 else
1824 {
1825 pHeader = (VMMDevHGCMRequestHeader *)RTMemAlloc (pCmd->cbSize);
1826 if (pHeader == NULL)
1827 {
1828 LogRel(("VMMDev: Failed to allocate %u bytes for HGCM request completion!!!\n", pCmd->cbSize));
1829
1830 /* Free it. The command have to be excluded from list of active commands anyway. */
1831 RTMemFree (pCmd);
1832 return;
1833 }
1834 }
1835
1836 /*
1837 * Enter and leave the critical section here so we make sure
1838 * vmmdevRequestHandler has completed before we read & write
1839 * the request. (This isn't 100% optimal, but it solves the
1840 * 3.0 blocker.)
1841 */
1842 /** @todo It would be faster if this interface would use MMIO2 memory and we
1843 * didn't have to mess around with PDMDevHlpPhysRead/Write. We're
1844 * reading the header 3 times now and writing the request back twice. */
1845
1846 PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
1847 PDMCritSectLeave(&pThis->CritSect);
1848
1849 PDMDevHlpPhysRead(pThis->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbSize);
1850
1851 /* Setup return codes. */
1852 pHeader->result = result;
1853
1854 /* Verify the request type. */
1855 rc = vmmdevHGCMCmdVerify (pCmd, pHeader);
1856
1857 if (RT_SUCCESS (rc))
1858 {
1859 /* Update parameters and data buffers. */
1860
1861 switch (pHeader->header.requestType)
1862 {
1863#ifdef VBOX_WITH_64_BITS_GUESTS
1864 case VMMDevReq_HGCMCall64:
1865 {
1866 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
1867
1868 uint32_t cParms = pHGCMCall->cParms;
1869
1870 VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms;
1871
1872 uint32_t i;
1873 uint32_t iLinPtr = 0;
1874
1875 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
1876
1877 for (i = 0; i < cParms; i++, pGuestParm++, pHostParm++)
1878 {
1879 switch (pGuestParm->type)
1880 {
1881 case VMMDevHGCMParmType_32bit:
1882 {
1883 pGuestParm->u.value32 = pHostParm->u.uint32;
1884 } break;
1885
1886 case VMMDevHGCMParmType_64bit:
1887 {
1888 pGuestParm->u.value64 = pHostParm->u.uint64;
1889 } break;
1890
1891 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1892 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1893 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1894 {
1895 /* Copy buffer back to guest memory. */
1896 uint32_t size = pGuestParm->u.Pointer.size;
1897
1898 if (size > 0)
1899 {
1900 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
1901 {
1902 /* Use the saved page list to write data back to the guest RAM. */
1903 rc = vmmdevHGCMWriteLinPtr (pThis->pDevIns, i, pHostParm->u.pointer.addr,
1904 size, iLinPtr, pCmd->paLinPtrs);
1905 AssertReleaseRC(rc);
1906 }
1907
1908 /* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
1909 iLinPtr++;
1910 }
1911 } break;
1912
1913 case VMMDevHGCMParmType_PageList:
1914 {
1915 uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
1916
1917 uint32_t size = pGuestParm->u.PageList.size;
1918
1919 /* Check that the page list info is within the request. */
1920 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
1921 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
1922 {
1923 rc = VERR_INVALID_PARAMETER;
1924 break;
1925 }
1926
1927 /* At least the structure is within. */
1928 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
1929
1930 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
1931
1932 if ( pPageListInfo->cPages == 0
1933 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
1934 {
1935 rc = VERR_INVALID_PARAMETER;
1936 break;
1937 }
1938
1939 if (size > 0)
1940 {
1941 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
1942 {
1943 /* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
1944 rc = vmmdevHGCMPageListWrite(pThis->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
1945 }
1946 else
1947 rc = VINF_SUCCESS;
1948 }
1949
1950 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
1951 } break;
1952
1953 default:
1954 {
1955 /* This indicates that the guest request memory was corrupted. */
1956 AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
1957 }
1958 }
1959 }
1960# ifdef VBOX_WITH_DTRACE
1961 idFunction = pHGCMCall->u32Function;
1962 idClient = pHGCMCall->u32ClientID;
1963# endif
1964 break;
1965 }
1966
1967 case VMMDevReq_HGCMCall32:
1968 {
1969 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
1970
1971 uint32_t cParms = pHGCMCall->cParms;
1972
1973 VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms;
1974
1975 uint32_t i;
1976 uint32_t iLinPtr = 0;
1977
1978 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
1979
1980 for (i = 0; i < cParms; i++, pGuestParm++, pHostParm++)
1981 {
1982 switch (pGuestParm->type)
1983 {
1984 case VMMDevHGCMParmType_32bit:
1985 {
1986 pGuestParm->u.value32 = pHostParm->u.uint32;
1987 } break;
1988
1989 case VMMDevHGCMParmType_64bit:
1990 {
1991 pGuestParm->u.value64 = pHostParm->u.uint64;
1992 } break;
1993
1994 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1995 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1996 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1997 {
1998 /* Copy buffer back to guest memory. */
1999 uint32_t size = pGuestParm->u.Pointer.size;
2000
2001 if (size > 0)
2002 {
2003 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
2004 {
2005 /* Use the saved page list to write data back to the guest RAM. */
2006 rc = vmmdevHGCMWriteLinPtr (pThis->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr, pCmd->paLinPtrs);
2007 AssertReleaseRC(rc);
2008 }
2009
2010 /* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
2011 iLinPtr++;
2012 }
2013 } break;
2014
2015 case VMMDevHGCMParmType_PageList:
2016 {
2017 uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
2018
2019 uint32_t size = pGuestParm->u.PageList.size;
2020
2021 /* Check that the page list info is within the request. */
2022 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
2023 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
2024 {
2025 rc = VERR_INVALID_PARAMETER;
2026 break;
2027 }
2028
2029 /* At least the structure is within. */
2030 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
2031
2032 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
2033
2034 if ( pPageListInfo->cPages == 0
2035 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
2036 {
2037 rc = VERR_INVALID_PARAMETER;
2038 break;
2039 }
2040
2041 if (size > 0)
2042 {
2043 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
2044 {
2045 /* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
2046 rc = vmmdevHGCMPageListWrite(pThis->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
2047 }
2048 else
2049 rc = VINF_SUCCESS;
2050 }
2051
2052 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
2053 } break;
2054
2055 default:
2056 {
2057 /* This indicates that the guest request memory was corrupted. */
2058 AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
2059 }
2060 }
2061 }
2062# ifdef VBOX_WITH_DTRACE
2063 idFunction = pHGCMCall->u32Function;
2064 idClient = pHGCMCall->u32ClientID;
2065# endif
2066 break;
2067 }
2068#else
2069 case VMMDevReq_HGCMCall:
2070 {
2071 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
2072
2073 uint32_t cParms = pHGCMCall->cParms;
2074
2075 VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms;
2076
2077 uint32_t i;
2078 uint32_t iLinPtr = 0;
2079
2080 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
2081
2082 for (i = 0; i < cParms; i++, pGuestParm++, pHostParm++)
2083 {
2084 switch (pGuestParm->type)
2085 {
2086 case VMMDevHGCMParmType_32bit:
2087 {
2088 pGuestParm->u.value32 = pHostParm->u.uint32;
2089 } break;
2090
2091 case VMMDevHGCMParmType_64bit:
2092 {
2093 pGuestParm->u.value64 = pHostParm->u.uint64;
2094 } break;
2095
2096 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
2097 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
2098 case VMMDevHGCMParmType_LinAddr: /* In & Out */
2099 {
2100 /* Copy buffer back to guest memory. */
2101 uint32_t size = pGuestParm->u.Pointer.size;
2102
2103 if (size > 0)
2104 {
2105 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
2106 {
2107 /* Use the saved page list to write data back to the guest RAM. */
2108 rc = vmmdevHGCMWriteLinPtr (pThis->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr, pCmd->paLinPtrs);
2109 AssertReleaseRC(rc);
2110 }
2111
2112 /* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
2113 iLinPtr++;
2114 }
2115 } break;
2116
2117 case VMMDevHGCMParmType_PageList:
2118 {
2119 uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
2120
2121 uint32_t size = pGuestParm->u.PageList.size;
2122
2123 /* Check that the page list info is within the request. */
2124 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
2125 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
2126 {
2127 rc = VERR_INVALID_PARAMETER;
2128 break;
2129 }
2130
2131 /* At least the structure is within. */
2132 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
2133
2134 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
2135
2136 if ( pPageListInfo->cPages == 0
2137 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
2138 {
2139 rc = VERR_INVALID_PARAMETER;
2140 break;
2141 }
2142
2143 if (size > 0)
2144 {
2145 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
2146 {
2147 /* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
2148 rc = vmmdevHGCMPageListWrite(pThis->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
2149 }
2150 else
2151 rc = VINF_SUCCESS;
2152 }
2153
2154 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
2155 } break;
2156
2157 default:
2158 {
2159 /* This indicates that the guest request memory was corrupted. */
2160 AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
2161 }
2162 }
2163 }
2164# ifdef VBOX_WITH_DTRACE
2165 idFunction = pHGCMCall->u32Function;
2166 idClient = pHGCMCall->u32ClientID;
2167# endif
2168 break;
2169 }
2170#endif /* VBOX_WITH_64_BITS_GUESTS */
2171 case VMMDevReq_HGCMConnect:
2172 {
2173 VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pCmd+1);
2174
2175 /* save the client id in the guest request packet */
2176 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)pHeader;
2177 pHGCMConnect->u32ClientID = pHGCMConnectCopy->u32ClientID;
2178 break;
2179 }
2180
2181 default:
2182 /* make gcc happy */
2183 break;
2184 }
2185 }
2186 else
2187 {
2188 /* Command type is wrong. Return error to the guest. */
2189 pHeader->header.rc = rc;
2190 }
2191
2192 /* Mark request as processed. */
2193 pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
2194
2195 /* Write back the request */
2196 PDMDevHlpPhysWrite(pThis->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbSize);
2197
2198 /* Now, when the command was removed from the internal list, notify the guest. */
2199 VMMDevNotifyGuest (pThis, VMMDEV_EVENT_HGCM);
2200
2201 if ((uint8_t *)pHeader != &au8Prealloc[0])
2202 {
2203 /* Only if it was allocated from heap. */
2204 RTMemFree (pHeader);
2205 }
2206 }
2207
2208 /* Deallocate the command memory. */
2209 if (pCmd->paLinPtrs)
2210 {
2211 RTMemFree (pCmd->paLinPtrs);
2212 }
2213
2214 RTMemFree (pCmd);
2215
2216 VBOXDD_HGCMCALL_COMPLETED_DONE(pCmd, idFunction, idClient, result);
2217 return;
2218}
2219
2220DECLCALLBACK(void) hgcmCompleted (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
2221{
2222 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
2223
2224 VBOXDD_HGCMCALL_COMPLETED_REQ(pCmd, result);
2225
2226/** @todo no longer necessary to forward to EMT, but it might be more
2227 * efficient...? */
2228 /* Not safe to execute asynchronously; forward to EMT */
2229 int rc = VMR3ReqCallVoidNoWait(PDMDevHlpGetVM(pThis->pDevIns), VMCPUID_ANY,
2230 (PFNRT)hgcmCompletedWorker, 3, pInterface, result, pCmd);
2231 AssertRC(rc);
2232}
2233
2234/** @thread EMT */
2235int vmmdevHGCMSaveState(PVMMDEV pThis, PSSMHANDLE pSSM)
2236{
2237 /* Save information about pending requests.
2238 * Only GCPtrs are of interest.
2239 */
2240 int rc = VINF_SUCCESS;
2241
2242 LogFlowFunc(("\n"));
2243
2244 /* Compute how many commands are pending. */
2245 uint32_t cCmds = 0;
2246
2247 PVBOXHGCMCMD pIter = pThis->pHGCMCmdList;
2248
2249 while (pIter)
2250 {
2251 LogFlowFunc (("pIter %p\n", pIter));
2252 cCmds++;
2253 pIter = pIter->pNext;
2254 }
2255
2256 LogFlowFunc(("cCmds = %d\n", cCmds));
2257
2258 /* Save number of commands. */
2259 rc = SSMR3PutU32(pSSM, cCmds);
2260 AssertRCReturn(rc, rc);
2261
2262 if (cCmds > 0)
2263 {
2264 pIter = pThis->pHGCMCmdList;
2265
2266 while (pIter)
2267 {
2268 PVBOXHGCMCMD pNext = pIter->pNext;
2269
2270 LogFlowFunc (("Saving %RGp, size %d\n", pIter->GCPhys, pIter->cbSize));
2271
2272 /* GC physical address of the guest request. */
2273 rc = SSMR3PutGCPhys(pSSM, pIter->GCPhys);
2274 AssertRCReturn(rc, rc);
2275
2276 /* Request packet size */
2277 rc = SSMR3PutU32(pSSM, pIter->cbSize);
2278 AssertRCReturn(rc, rc);
2279
2280 /*
2281 * Version 9+: save complete information about commands.
2282 */
2283
2284 /* The type of the command. */
2285 rc = SSMR3PutU32(pSSM, (uint32_t)pIter->enmCmdType);
2286 AssertRCReturn(rc, rc);
2287
2288 /* Whether the command was cancelled by the guest. */
2289 rc = SSMR3PutBool(pSSM, pIter->fCancelled);
2290 AssertRCReturn(rc, rc);
2291
2292 /* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */
2293 rc = SSMR3PutU32(pSSM, (uint32_t)pIter->cLinPtrs);
2294 AssertRCReturn(rc, rc);
2295
2296 if (pIter->cLinPtrs > 0)
2297 {
2298 /* How many pages for all linptrs in this command. */
2299 rc = SSMR3PutU32(pSSM, (uint32_t)pIter->cLinPtrPages);
2300 AssertRCReturn(rc, rc);
2301 }
2302
2303 int i;
2304 for (i = 0; i < pIter->cLinPtrs; i++)
2305 {
2306 /* Pointer to descriptions of linear pointers. */
2307 VBOXHGCMLINPTR *pLinPtr = &pIter->paLinPtrs[i];
2308
2309 /* Index of the parameter. */
2310 rc = SSMR3PutU32(pSSM, (uint32_t)pLinPtr->iParm);
2311 AssertRCReturn(rc, rc);
2312
2313 /* Offset in the first physical page of the region. */
2314 rc = SSMR3PutU32(pSSM, pLinPtr->offFirstPage);
2315 AssertRCReturn(rc, rc);
2316
2317 /* How many pages. */
2318 rc = SSMR3PutU32(pSSM, pLinPtr->cPages);
2319 AssertRCReturn(rc, rc);
2320
2321 uint32_t iPage;
2322 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
2323 {
2324 /* Array of the GC physical addresses for these pages.
2325 * It is assumed that the physical address of the locked resident
2326 * guest page does not change.
2327 */
2328 rc = SSMR3PutGCPhys(pSSM, pLinPtr->paPages[iPage]);
2329 AssertRCReturn(rc, rc);
2330 }
2331 }
2332
2333 /* A reserved field, will allow to extend saved data for a command. */
2334 rc = SSMR3PutU32(pSSM, 0);
2335 AssertRCReturn(rc, rc);
2336
2337 /* Mark the command as saved to make sure that it will not be
2338 * completed later by a still running host service.
2339 */
2340 pIter->fSaved = true;
2341 vmmdevHGCMRemoveCommand (pThis, pIter);
2342
2343 pIter = pNext;
2344 }
2345 }
2346
2347 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
2348 rc = SSMR3PutU32(pSSM, 0);
2349 AssertRCReturn(rc, rc);
2350
2351 return rc;
2352}
2353
2354/** @thread EMT(0) */
2355int vmmdevHGCMLoadState(PVMMDEV pThis, PSSMHANDLE pSSM, uint32_t uVersion)
2356{
2357 int rc = VINF_SUCCESS;
2358
2359 LogFlowFunc(("\n"));
2360
2361 /* Read how many commands were pending. */
2362 uint32_t cCmds = 0;
2363 rc = SSMR3GetU32(pSSM, &cCmds);
2364 AssertRCReturn(rc, rc);
2365
2366 LogFlowFunc(("cCmds = %d\n", cCmds));
2367
2368 if (uVersion < 9)
2369 {
2370 /* Only the guest physical address is saved. */
2371 while (cCmds--)
2372 {
2373 RTGCPHYS GCPhys;
2374 uint32_t cbSize;
2375
2376 rc = SSMR3GetGCPhys(pSSM, &GCPhys);
2377 AssertRCReturn(rc, rc);
2378
2379 rc = SSMR3GetU32(pSSM, &cbSize);
2380 AssertRCReturn(rc, rc);
2381
2382 LogFlowFunc (("Restoring %RGp size %x bytes\n", GCPhys, cbSize));
2383
2384 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (sizeof (struct VBOXHGCMCMD));
2385 AssertReturn(pCmd, VERR_NO_MEMORY);
2386
2387 pCmd->enmCmdType = VBOXHGCMCMDTYPE_LOADSTATE; /* This marks the "old" saved command. */
2388
2389 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, cbSize, VBOXHGCMCMDTYPE_LOADSTATE);
2390 }
2391 }
2392 else
2393 {
2394 /*
2395 * Version 9+: Load complete information about commands.
2396 */
2397 uint32_t u32;
2398 bool f;
2399
2400 while (cCmds--)
2401 {
2402 RTGCPHYS GCPhys;
2403 uint32_t cbSize;
2404
2405 /* GC physical address of the guest request. */
2406 rc = SSMR3GetGCPhys(pSSM, &GCPhys);
2407 AssertRCReturn(rc, rc);
2408
2409 /* The request packet size */
2410 rc = SSMR3GetU32(pSSM, &cbSize);
2411 AssertRCReturn(rc, rc);
2412
2413 LogFlowFunc (("Restoring %RGp size %x bytes\n", GCPhys, cbSize));
2414
2415 /* For uVersion <= 12, this was the size of entire command.
2416 * Now the size is recalculated in vmmdevHGCMLoadStateDone.
2417 */
2418 if (uVersion <= 12)
2419 {
2420 rc = SSMR3Skip(pSSM, sizeof (uint32_t));
2421 AssertRCReturn(rc, rc);
2422 }
2423
2424 /* Allocate only VBOXHGCMCMD structure. vmmdevHGCMLoadStateDone will reallocate the command
2425 * with additional space for parameters and for pointer/pagelists buffer.
2426 */
2427 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (sizeof (VBOXHGCMCMD));
2428 AssertReturn(pCmd, VERR_NO_MEMORY);
2429
2430 /* The type of the command. */
2431 rc = SSMR3GetU32(pSSM, &u32);
2432 AssertRCReturn(rc, rc);
2433 pCmd->enmCmdType = (VBOXHGCMCMDTYPE)u32;
2434
2435 /* Whether the command was cancelled by the guest. */
2436 rc = SSMR3GetBool(pSSM, &f);
2437 AssertRCReturn(rc, rc);
2438 pCmd->fCancelled = f;
2439
2440 /* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */
2441 rc = SSMR3GetU32(pSSM, &u32);
2442 AssertRCReturn(rc, rc);
2443 pCmd->cLinPtrs = u32;
2444
2445 if (pCmd->cLinPtrs > 0)
2446 {
2447 /* How many pages for all linptrs in this command. */
2448 rc = SSMR3GetU32(pSSM, &u32);
2449 AssertRCReturn(rc, rc);
2450 pCmd->cLinPtrPages = u32;
2451
2452 pCmd->paLinPtrs = (VBOXHGCMLINPTR *)RTMemAllocZ ( sizeof (VBOXHGCMLINPTR) * pCmd->cLinPtrs
2453 + sizeof (RTGCPHYS) * pCmd->cLinPtrPages);
2454 AssertReturn(pCmd->paLinPtrs, VERR_NO_MEMORY);
2455
2456 RTGCPHYS *pPages = (RTGCPHYS *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) * pCmd->cLinPtrs);
2457 int cPages = 0;
2458
2459 int i;
2460 for (i = 0; i < pCmd->cLinPtrs; i++)
2461 {
2462 /* Pointer to descriptions of linear pointers. */
2463 VBOXHGCMLINPTR *pLinPtr = &pCmd->paLinPtrs[i];
2464
2465 pLinPtr->paPages = pPages;
2466
2467 /* Index of the parameter. */
2468 rc = SSMR3GetU32(pSSM, &u32);
2469 AssertRCReturn(rc, rc);
2470 pLinPtr->iParm = u32;
2471
2472 /* Offset in the first physical page of the region. */
2473 rc = SSMR3GetU32(pSSM, &u32);
2474 AssertRCReturn(rc, rc);
2475 pLinPtr->offFirstPage = u32;
2476
2477 /* How many pages. */
2478 rc = SSMR3GetU32(pSSM, &u32);
2479 AssertRCReturn(rc, rc);
2480 pLinPtr->cPages = u32;
2481
2482 uint32_t iPage;
2483 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
2484 {
2485 /* Array of the GC physical addresses for these pages.
2486 * It is assumed that the physical address of the locked resident
2487 * guest page does not change.
2488 */
2489 RTGCPHYS GCPhysPage;
2490 rc = SSMR3GetGCPhys(pSSM, &GCPhysPage);
2491 AssertRCReturn(rc, rc);
2492
2493 /* Verify that the number of loaded pages is valid. */
2494 cPages++;
2495 if (cPages > pCmd->cLinPtrPages)
2496 {
2497 LogRel(("VMMDevHGCM load state failure: cPages %d, expected %d, ptr %d/%d\n",
2498 cPages, pCmd->cLinPtrPages, i, pCmd->cLinPtrs));
2499 return VERR_SSM_UNEXPECTED_DATA;
2500 }
2501
2502 *pPages++ = GCPhysPage;
2503 }
2504 }
2505 }
2506
2507 /* A reserved field, will allow to extend saved data for a command. */
2508 rc = SSMR3GetU32(pSSM, &u32);
2509 AssertRCReturn(rc, rc);
2510
2511 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, cbSize, VBOXHGCMCMDTYPE_LOADSTATE);
2512 }
2513
2514 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
2515 rc = SSMR3GetU32(pSSM, &u32);
2516 AssertRCReturn(rc, rc);
2517 }
2518
2519 return rc;
2520}
2521
2522/** @thread EMT */
2523int vmmdevHGCMLoadStateDone(PVMMDEV pThis, PSSMHANDLE pSSM)
2524{
2525 LogFlowFunc(("\n"));
2526
2527 /* Reissue pending requests. */
2528 PPDMDEVINS pDevIns = pThis->pDevIns;
2529
2530 int rc = vmmdevHGCMCmdListLock (pThis);
2531
2532 if (RT_SUCCESS (rc))
2533 {
2534 /* Start from the current list head and commands loaded from saved state.
2535 * New commands will be inserted at the list head, so they will not be seen by
2536 * this loop.
2537 *
2538 * Note: The list contains only VBOXHGCMCMD structures, place for HGCM parameters
2539 * and for data buffers has not been allocated.
2540 * Command handlers compute the command size and reallocate it before
2541 * resubmitting the command to HGCM services.
2542 * New commands will be inserted to the list.
2543 */
2544 PVBOXHGCMCMD pIter = pThis->pHGCMCmdList;
2545
2546 pThis->pHGCMCmdList = NULL; /* Reset the list. Saved commands will be processed and deallocated. */
2547
2548 while (pIter)
2549 {
2550 /* This will remove the command from the list if resubmitting fails. */
2551 bool fHGCMCalled = false;
2552
2553 LogFlowFunc (("pIter %p\n", pIter));
2554
2555 PVBOXHGCMCMD pNext = pIter->pNext;
2556
2557 PVBOXHGCMCMD pCmd = NULL; /* Resubmitted command. */
2558
2559 VMMDevHGCMRequestHeader *requestHeader = (VMMDevHGCMRequestHeader *)RTMemAllocZ (pIter->cbSize);
2560 Assert(requestHeader);
2561 if (requestHeader == NULL)
2562 return VERR_NO_MEMORY;
2563
2564 PDMDevHlpPhysRead(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize);
2565
2566 /* the structure size must be greater or equal to the header size */
2567 if (requestHeader->header.size < sizeof(VMMDevHGCMRequestHeader))
2568 {
2569 Log(("VMMDev request header size too small! size = %d\n", requestHeader->header.size));
2570 }
2571 else
2572 {
2573 /* check the version of the header structure */
2574 if (requestHeader->header.version != VMMDEV_REQUEST_HEADER_VERSION)
2575 {
2576 Log(("VMMDev: guest header version (0x%08X) differs from ours (0x%08X)\n", requestHeader->header.version, VMMDEV_REQUEST_HEADER_VERSION));
2577 }
2578 else
2579 {
2580 Log(("VMMDev request issued: %d, command type %d\n", requestHeader->header.requestType, pIter->enmCmdType));
2581
2582 /* Use the saved command type. Even if the guest has changed the memory already,
2583 * HGCM should see the same command as it was before saving state.
2584 */
2585 switch (pIter->enmCmdType)
2586 {
2587 case VBOXHGCMCMDTYPE_CONNECT:
2588 {
2589 if (requestHeader->header.size < sizeof(VMMDevHGCMConnect))
2590 {
2591 AssertMsgFailed(("VMMDevReq_HGCMConnect structure has invalid size!\n"));
2592 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2593 }
2594 else if (!pThis->pHGCMDrv)
2595 {
2596 Log(("VMMDevReq_HGCMConnect HGCM Connector is NULL!\n"));
2597 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2598 }
2599 else
2600 {
2601 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)requestHeader;
2602
2603 Log(("VMMDevReq_HGCMConnect\n"));
2604
2605 requestHeader->header.rc = vmmdevHGCMConnectSaved (pThis, pHGCMConnect, pIter->GCPhys, &fHGCMCalled, pIter, &pCmd);
2606 }
2607 break;
2608 }
2609
2610 case VBOXHGCMCMDTYPE_DISCONNECT:
2611 {
2612 if (requestHeader->header.size < sizeof(VMMDevHGCMDisconnect))
2613 {
2614 AssertMsgFailed(("VMMDevReq_HGCMDisconnect structure has invalid size!\n"));
2615 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2616 }
2617 else if (!pThis->pHGCMDrv)
2618 {
2619 Log(("VMMDevReq_HGCMDisconnect HGCM Connector is NULL!\n"));
2620 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2621 }
2622 else
2623 {
2624 VMMDevHGCMDisconnect *pHGCMDisconnect = (VMMDevHGCMDisconnect *)requestHeader;
2625
2626 Log(("VMMDevReq_VMMDevHGCMDisconnect\n"));
2627 requestHeader->header.rc = vmmdevHGCMDisconnectSaved (pThis, pHGCMDisconnect, pIter->GCPhys, &fHGCMCalled, pIter, &pCmd);
2628 }
2629 break;
2630 }
2631
2632 case VBOXHGCMCMDTYPE_CALL:
2633 {
2634 if (requestHeader->header.size < sizeof(VMMDevHGCMCall))
2635 {
2636 AssertMsgFailed(("VMMDevReq_HGCMCall structure has invalid size!\n"));
2637 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2638 }
2639 else if (!pThis->pHGCMDrv)
2640 {
2641 Log(("VMMDevReq_HGCMCall HGCM Connector is NULL!\n"));
2642 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2643 }
2644 else
2645 {
2646 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)requestHeader;
2647
2648 Log(("VMMDevReq_HGCMCall: sizeof (VMMDevHGCMRequest) = %04X\n", sizeof (VMMDevHGCMCall)));
2649
2650 Log(("%.*Rhxd\n", requestHeader->header.size, requestHeader));
2651
2652#ifdef VBOX_WITH_64_BITS_GUESTS
2653 bool f64Bits = (requestHeader->header.requestType == VMMDevReq_HGCMCall64);
2654#else
2655 bool f64Bits = false;
2656#endif /* VBOX_WITH_64_BITS_GUESTS */
2657 requestHeader->header.rc = vmmdevHGCMCallSaved (pThis, pHGCMCall, pIter->GCPhys, requestHeader->header.size, f64Bits, &fHGCMCalled, pIter, &pCmd);
2658 }
2659 break;
2660 }
2661 case VBOXHGCMCMDTYPE_LOADSTATE:
2662 {
2663 /* Old saved state. */
2664 switch (requestHeader->header.requestType)
2665 {
2666 case VMMDevReq_HGCMConnect:
2667 {
2668 if (requestHeader->header.size < sizeof(VMMDevHGCMConnect))
2669 {
2670 AssertMsgFailed(("VMMDevReq_HGCMConnect structure has invalid size!\n"));
2671 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2672 }
2673 else if (!pThis->pHGCMDrv)
2674 {
2675 Log(("VMMDevReq_HGCMConnect HGCM Connector is NULL!\n"));
2676 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2677 }
2678 else
2679 {
2680 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)requestHeader;
2681
2682 Log(("VMMDevReq_HGCMConnect\n"));
2683
2684 requestHeader->header.rc = vmmdevHGCMConnect (pThis, pHGCMConnect, pIter->GCPhys);
2685 }
2686 break;
2687 }
2688
2689 case VMMDevReq_HGCMDisconnect:
2690 {
2691 if (requestHeader->header.size < sizeof(VMMDevHGCMDisconnect))
2692 {
2693 AssertMsgFailed(("VMMDevReq_HGCMDisconnect structure has invalid size!\n"));
2694 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2695 }
2696 else if (!pThis->pHGCMDrv)
2697 {
2698 Log(("VMMDevReq_HGCMDisconnect HGCM Connector is NULL!\n"));
2699 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2700 }
2701 else
2702 {
2703 VMMDevHGCMDisconnect *pHGCMDisconnect = (VMMDevHGCMDisconnect *)requestHeader;
2704
2705 Log(("VMMDevReq_VMMDevHGCMDisconnect\n"));
2706 requestHeader->header.rc = vmmdevHGCMDisconnect (pThis, pHGCMDisconnect, pIter->GCPhys);
2707 }
2708 break;
2709 }
2710
2711#ifdef VBOX_WITH_64_BITS_GUESTS
2712 case VMMDevReq_HGCMCall64:
2713 case VMMDevReq_HGCMCall32:
2714#else
2715 case VMMDevReq_HGCMCall:
2716#endif /* VBOX_WITH_64_BITS_GUESTS */
2717 {
2718 if (requestHeader->header.size < sizeof(VMMDevHGCMCall))
2719 {
2720 AssertMsgFailed(("VMMDevReq_HGCMCall structure has invalid size!\n"));
2721 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2722 }
2723 else if (!pThis->pHGCMDrv)
2724 {
2725 Log(("VMMDevReq_HGCMCall HGCM Connector is NULL!\n"));
2726 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2727 }
2728 else
2729 {
2730 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)requestHeader;
2731
2732 Log(("VMMDevReq_HGCMCall: sizeof (VMMDevHGCMRequest) = %04X\n", sizeof (VMMDevHGCMCall)));
2733
2734 Log(("%.*Rhxd\n", requestHeader->header.size, requestHeader));
2735
2736#ifdef VBOX_WITH_64_BITS_GUESTS
2737 bool f64Bits = (requestHeader->header.requestType == VMMDevReq_HGCMCall64);
2738#else
2739 bool f64Bits = false;
2740#endif /* VBOX_WITH_64_BITS_GUESTS */
2741 requestHeader->header.rc = vmmdevHGCMCall (pThis, pHGCMCall, requestHeader->header.size, pIter->GCPhys, f64Bits);
2742 }
2743 break;
2744 }
2745 default:
2746 AssertMsgFailed(("Unknown request type %x during LoadState\n", requestHeader->header.requestType));
2747 LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->header.requestType));
2748 }
2749 } break;
2750
2751 default:
2752 AssertMsgFailed(("Unknown request type %x during LoadState\n", requestHeader->header.requestType));
2753 LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->header.requestType));
2754 }
2755 }
2756 }
2757
2758 if (pIter->enmCmdType == VBOXHGCMCMDTYPE_LOADSTATE)
2759 {
2760 /* Old saved state. */
2761
2762 /* Write back the request */
2763 PDMDevHlpPhysWrite(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize);
2764 RTMemFree(requestHeader);
2765 requestHeader = NULL;
2766 }
2767 else
2768 {
2769 if (!fHGCMCalled)
2770 {
2771 /* HGCM was not called. Return the error to the guest. Guest may try to repeat the call. */
2772 requestHeader->header.rc = VERR_TRY_AGAIN;
2773 requestHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
2774 }
2775
2776 /* Write back the request */
2777 PDMDevHlpPhysWrite(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize);
2778 RTMemFree(requestHeader);
2779 requestHeader = NULL;
2780
2781 if (!fHGCMCalled)
2782 {
2783 /* HGCM was not called. Deallocate the current command and then notify guest. */
2784 if (pCmd)
2785 {
2786 vmmdevHGCMRemoveCommand (pThis, pCmd);
2787
2788 if (pCmd->paLinPtrs != NULL)
2789 {
2790 RTMemFree(pCmd->paLinPtrs);
2791 }
2792
2793 RTMemFree(pCmd);
2794 pCmd = NULL;
2795 }
2796
2797 VMMDevNotifyGuest (pThis, VMMDEV_EVENT_HGCM);
2798 }
2799 }
2800
2801 /* Deallocate the saved command structure. */
2802 if (pIter->paLinPtrs != NULL)
2803 {
2804 RTMemFree(pIter->paLinPtrs);
2805 }
2806
2807 RTMemFree(pIter);
2808
2809 pIter = pNext;
2810 }
2811
2812 vmmdevHGCMCmdListUnlock (pThis);
2813 }
2814
2815 return rc;
2816}
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