VirtualBox

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

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

Moving the backdoor logging and timesync down so the request handling is in one big block at the top of the file, rather than split by three I/O handlers. Documented the IRQ related functions, realizing that the _EMT bits no longer applies (and wasn't a requirement since a very long time (PIC/PCI IRQ raising was only done by EMT at some point). The critsect takes care of serialization. VMMDevState * -> PVMMDEV.

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

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