VirtualBox

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

Last change on this file since 33000 was 29743, checked in by vboxsync, 14 years ago

HGCM: fixed memory leak (xTracker 4961).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use