VirtualBox

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

Last change on this file since 50709 was 49846, checked in by vboxsync, 11 years ago

HGCM: check size.

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

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