VirtualBox

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

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

HGCM param verification

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 107.5 KB
Line 
1/* $Id: VMMDevHGCM.cpp 49454 2013-11-12 16:24:07Z vboxsync $ */
2/** @file
3 * VMMDev - HGCM - Host-Guest Communication Manager Device.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DEV_VMM
23#include <iprt/alloc.h>
24#include <iprt/asm.h>
25#include <iprt/assert.h>
26#include <iprt/param.h>
27#include <iprt/string.h>
28
29#include <VBox/err.h>
30#include <VBox/hgcmsvc.h>
31
32#include <VBox/log.h>
33
34#include "VMMDevHGCM.h"
35
36#ifdef VBOX_WITH_DTRACE
37# include "dtrace/VBoxDD.h"
38#else
39# define VBOXDD_HGCMCALL_ENTER(a,b,c,d) do { } while (0)
40# define VBOXDD_HGCMCALL_COMPLETED_REQ(a,b) do { } while (0)
41# define VBOXDD_HGCMCALL_COMPLETED_EMT(a,b) do { } while (0)
42# define VBOXDD_HGCMCALL_COMPLETED_DONE(a,b,c,d) do { } while (0)
43#endif
44
45
46/*******************************************************************************
47* Structures and Typedefs *
48*******************************************************************************/
49typedef enum VBOXHGCMCMDTYPE
50{
51 VBOXHGCMCMDTYPE_LOADSTATE = 0,
52 VBOXHGCMCMDTYPE_CONNECT,
53 VBOXHGCMCMDTYPE_DISCONNECT,
54 VBOXHGCMCMDTYPE_CALL,
55 VBOXHGCMCMDTYPE_SizeHack = 0x7fffffff
56} VBOXHGCMCMDTYPE;
57
58/**
59 * Information about a linear ptr parameter.
60 */
61typedef struct VBOXHGCMLINPTR
62{
63 /** Index of the parameter. */
64 uint32_t iParm;
65
66 /** Offset in the first physical page of the region. */
67 uint32_t offFirstPage;
68
69 /** How many pages. */
70 uint32_t cPages;
71
72 /** Pointer to array of the GC physical addresses for these pages.
73 * It is assumed that the physical address of the locked resident guest page
74 * does not change.
75 */
76 RTGCPHYS *paPages;
77
78} VBOXHGCMLINPTR;
79
80struct VBOXHGCMCMD
81{
82 /** Active commands, list is protected by critsectHGCMCmdList. */
83 struct VBOXHGCMCMD *pNext;
84 struct VBOXHGCMCMD *pPrev;
85
86 /** The type of the command. */
87 VBOXHGCMCMDTYPE enmCmdType;
88
89 /** Whether the command was cancelled by the guest. */
90 bool fCancelled;
91
92 /** Whether the command is in the active commands list. */
93 bool fInList;
94
95 /** Whether the command was saved. */
96 bool fSaved;
97
98 /** GC physical address of the guest request. */
99 RTGCPHYS GCPhys;
100
101 /** Request packet size */
102 uint32_t cbSize;
103
104 /** Pointer to converted host parameters in case of a Call request.
105 * Parameters follow this structure in the same memory block.
106 */
107 VBOXHGCMSVCPARM *paHostParms;
108
109 /* 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 cbCmdSize += pGuestParm->u.Pointer.size;
689
690 cLinPtrs++;
691 /* Take the offset into the current page also into account! */
692 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
693 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
694 }
695
696 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
697 } break;
698
699 case VMMDevHGCMParmType_PageList:
700 {
701 cbCmdSize += pGuestParm->u.PageList.size;
702 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
703 } break;
704
705 case VMMDevHGCMParmType_32bit:
706 case VMMDevHGCMParmType_64bit:
707 {
708 } break;
709
710 default:
711 case VMMDevHGCMParmType_PhysAddr:
712 {
713 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
714 rc = VERR_INVALID_PARAMETER;
715 break;
716 }
717 }
718 }
719 }
720 else
721 {
722#ifdef VBOX_WITH_64_BITS_GUESTS
723 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
724#else
725 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
726#endif /* VBOX_WITH_64_BITS_GUESTS */
727
728 /* Look for pointer parameters, which require a host buffer. */
729 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
730 {
731 switch (pGuestParm->type)
732 {
733 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
734 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
735 case VMMDevHGCMParmType_LinAddr: /* In & Out */
736 {
737 if (pGuestParm->u.Pointer.size > 0)
738 {
739 /* Only pointers with some actual data are counted. */
740 cbCmdSize += pGuestParm->u.Pointer.size;
741
742 cLinPtrs++;
743 /* Take the offset into the current page also into account! */
744 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
745 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
746 }
747
748 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
749 } break;
750
751 case VMMDevHGCMParmType_PageList:
752 {
753 cbCmdSize += pGuestParm->u.PageList.size;
754 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
755 } break;
756
757 case VMMDevHGCMParmType_32bit:
758 case VMMDevHGCMParmType_64bit:
759 {
760 } break;
761
762 default:
763 {
764 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
765 rc = VERR_INVALID_PARAMETER;
766 break;
767 }
768 }
769 }
770 }
771
772 if (RT_FAILURE (rc))
773 {
774 return rc;
775 }
776
777 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAlloc (cbCmdSize);
778
779 if (pCmd == NULL)
780 {
781 return VERR_NO_MEMORY;
782 }
783
784 memset (pCmd, 0, sizeof (*pCmd));
785
786 pCmd->paHostParms = NULL;
787 pCmd->cLinPtrs = cLinPtrs;
788 pCmd->cLinPtrPages = cLinPtrPages;
789
790 if (cLinPtrs > 0)
791 {
792 pCmd->paLinPtrs = (VBOXHGCMLINPTR *)RTMemAlloc ( sizeof (VBOXHGCMLINPTR) * cLinPtrs
793 + sizeof (RTGCPHYS) * cLinPtrPages);
794
795 if (pCmd->paLinPtrs == NULL)
796 {
797 RTMemFree (pCmd);
798 return VERR_NO_MEMORY;
799 }
800 }
801 else
802 {
803 pCmd->paLinPtrs = NULL;
804 }
805
806 VBOXDD_HGCMCALL_ENTER(pCmd, pHGCMCall->u32Function, pHGCMCall->u32ClientID, cbCmdSize);
807
808 /* Process parameters, changing them to host context pointers for easy
809 * processing by connector. Guest must insure that the pointed data is actually
810 * in the guest RAM and remains locked there for entire request processing.
811 */
812
813 if (cParms != 0)
814 {
815 /* Compute addresses of host parms array and first memory buffer. */
816 VBOXHGCMSVCPARM *pHostParm = (VBOXHGCMSVCPARM *)((char *)pCmd + sizeof (struct VBOXHGCMCMD));
817
818 uint8_t *pcBuf = (uint8_t *)pHostParm + cParms * sizeof (VBOXHGCMSVCPARM);
819
820 pCmd->paHostParms = pHostParm;
821 pCmd->cHostParms = cParms;
822
823 uint32_t iLinPtr = 0;
824 RTGCPHYS *pPages = (RTGCPHYS *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) *cLinPtrs);
825
826 if (f64Bits)
827 {
828#ifdef VBOX_WITH_64_BITS_GUESTS
829 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
830#else
831 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
832 AssertFailed (); /* This code should not be called in this case */
833#endif /* VBOX_WITH_64_BITS_GUESTS */
834
835
836 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
837 {
838 switch (pGuestParm->type)
839 {
840 case VMMDevHGCMParmType_32bit:
841 {
842 uint32_t u32 = pGuestParm->u.value32;
843
844 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
845 pHostParm->u.uint32 = u32;
846
847 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
848 break;
849 }
850
851 case VMMDevHGCMParmType_64bit:
852 {
853 uint64_t u64 = pGuestParm->u.value64;
854
855 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
856 pHostParm->u.uint64 = u64;
857
858 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
859 break;
860 }
861
862 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
863 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
864 case VMMDevHGCMParmType_LinAddr: /* In & Out */
865 {
866 uint32_t size = pGuestParm->u.Pointer.size;
867 RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr;
868
869 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
870 pHostParm->u.pointer.size = size;
871
872 /* Copy guest data to an allocated buffer, so
873 * services can use the data.
874 */
875
876 if (size == 0)
877 {
878 pHostParm->u.pointer.addr = NULL;
879 }
880 else
881 {
882 /* Don't overdo it */
883 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
884 rc = PDMDevHlpPhysReadGCVirt(pThis->pDevIns, pcBuf, linearAddr, size);
885 else
886 rc = VINF_SUCCESS;
887
888 if (RT_SUCCESS(rc))
889 {
890 pHostParm->u.pointer.addr = pcBuf;
891 pcBuf += size;
892
893 /* Remember the guest physical pages that belong to the virtual address region.
894 * Do it for all linear pointers because load state will require In pointer info too.
895 */
896 rc = vmmdevHGCMSaveLinPtr (pThis->pDevIns, i, linearAddr, size, iLinPtr, pCmd->paLinPtrs, &pPages);
897
898 iLinPtr++;
899 }
900 }
901
902 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", linearAddr, rc));
903 break;
904 }
905
906 case VMMDevHGCMParmType_PageList:
907 {
908 uint32_t size = pGuestParm->u.PageList.size;
909
910 /* Check that the page list info is within the request. */
911 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
912 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
913 {
914 rc = VERR_INVALID_PARAMETER;
915 break;
916 }
917
918 /* At least the structure is within. */
919 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
920
921 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
922
923 if ( pPageListInfo->cPages == 0
924 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
925 {
926 rc = VERR_INVALID_PARAMETER;
927 break;
928 }
929
930 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
931 pHostParm->u.pointer.size = size;
932
933 /* Copy guest data to an allocated buffer, so
934 * services can use the data.
935 */
936
937 if (size == 0)
938 {
939 pHostParm->u.pointer.addr = NULL;
940 }
941 else
942 {
943 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
944 {
945 /* Copy pages to the pcBuf[size]. */
946 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pcBuf, size, pPageListInfo);
947 }
948 else
949 rc = VINF_SUCCESS;
950
951 if (RT_SUCCESS(rc))
952 {
953 pHostParm->u.pointer.addr = pcBuf;
954 pcBuf += size;
955 }
956 }
957
958 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
959 break;
960 }
961
962 /* just to shut up gcc */
963 default:
964 AssertFailed();
965 break;
966 }
967 }
968 }
969 else
970 {
971#ifdef VBOX_WITH_64_BITS_GUESTS
972 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
973#else
974 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
975#endif /* VBOX_WITH_64_BITS_GUESTS */
976
977 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
978 {
979 switch (pGuestParm->type)
980 {
981 case VMMDevHGCMParmType_32bit:
982 {
983 uint32_t u32 = pGuestParm->u.value32;
984
985 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
986 pHostParm->u.uint32 = u32;
987
988 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
989 break;
990 }
991
992 case VMMDevHGCMParmType_64bit:
993 {
994 uint64_t u64 = pGuestParm->u.value64;
995
996 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
997 pHostParm->u.uint64 = u64;
998
999 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
1000 break;
1001 }
1002
1003 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1004 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1005 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1006 {
1007 uint32_t size = pGuestParm->u.Pointer.size;
1008 RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr;
1009
1010 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1011 pHostParm->u.pointer.size = size;
1012
1013 /* Copy guest data to an allocated buffer, so
1014 * services can use the data.
1015 */
1016
1017 if (size == 0)
1018 {
1019 pHostParm->u.pointer.addr = NULL;
1020 }
1021 else
1022 {
1023 /* Don't overdo it */
1024 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
1025 rc = PDMDevHlpPhysReadGCVirt(pThis->pDevIns, pcBuf, linearAddr, size);
1026 else
1027 rc = VINF_SUCCESS;
1028
1029 if (RT_SUCCESS(rc))
1030 {
1031 pHostParm->u.pointer.addr = pcBuf;
1032 pcBuf += size;
1033
1034 /* Remember the guest physical pages that belong to the virtual address region.
1035 * Do it for all linear pointers because load state will require In pointer info too.
1036 */
1037 rc = vmmdevHGCMSaveLinPtr (pThis->pDevIns, i, linearAddr, size, iLinPtr, pCmd->paLinPtrs, &pPages);
1038
1039 iLinPtr++;
1040 }
1041 }
1042
1043 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n", linearAddr, rc));
1044 break;
1045 }
1046
1047 case VMMDevHGCMParmType_PageList:
1048 {
1049 uint32_t size = pGuestParm->u.PageList.size;
1050
1051 /* Check that the page list info is within the request. */
1052 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
1053 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
1054 {
1055 rc = VERR_INVALID_PARAMETER;
1056 break;
1057 }
1058
1059 /* At least the structure is within. */
1060 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
1061
1062 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
1063
1064 if ( pPageListInfo->cPages == 0
1065 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
1066 {
1067 rc = VERR_INVALID_PARAMETER;
1068 break;
1069 }
1070
1071 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1072 pHostParm->u.pointer.size = size;
1073
1074 /* Copy guest data to an allocated buffer, so
1075 * services can use the data.
1076 */
1077
1078 if (size == 0)
1079 {
1080 pHostParm->u.pointer.addr = NULL;
1081 }
1082 else
1083 {
1084 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
1085 {
1086 /* Copy pages to the pcBuf[size]. */
1087 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pcBuf, size, pPageListInfo);
1088 }
1089 else
1090 rc = VINF_SUCCESS;
1091
1092 if (RT_SUCCESS(rc))
1093 {
1094 pHostParm->u.pointer.addr = pcBuf;
1095 pcBuf += size;
1096 }
1097 }
1098
1099 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
1100 break;
1101 }
1102
1103 /* just to shut up gcc */
1104 default:
1105 AssertFailed();
1106 break;
1107 }
1108 }
1109 }
1110 }
1111
1112 if (RT_SUCCESS (rc))
1113 {
1114 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMCall->header.header.size, VBOXHGCMCMDTYPE_CALL);
1115
1116 /* Pass the function call to HGCM connector for actual processing */
1117 rc = pThis->pHGCMDrv->pfnCall (pThis->pHGCMDrv, pCmd, pHGCMCall->u32ClientID,
1118 pHGCMCall->u32Function, cParms, pCmd->paHostParms);
1119
1120 if (RT_FAILURE (rc))
1121 {
1122 Log(("vmmdevHGCMCall: pfnCall failed rc = %Rrc\n", rc));
1123 vmmdevHGCMRemoveCommand (pThis, pCmd);
1124 }
1125 }
1126
1127 if (RT_FAILURE (rc))
1128 {
1129 if (pCmd->paLinPtrs)
1130 {
1131 RTMemFree (pCmd->paLinPtrs);
1132 }
1133
1134 RTMemFree (pCmd);
1135 }
1136
1137 return rc;
1138}
1139
1140static void logRelLoadStatePointerIndexMismatch (uint32_t iParm, uint32_t iSavedParm, int iLinPtr, int cLinPtrs)
1141{
1142 LogRel(("Warning: VMMDev load state: a pointer parameter index mismatch %d (expected %d) (%d/%d)\n",
1143 (int)iParm, (int)iSavedParm, iLinPtr, cLinPtrs));
1144}
1145
1146static void logRelLoadStateBufferSizeMismatch (uint32_t size, uint32_t iPage, uint32_t cPages)
1147{
1148 LogRel(("Warning: VMMDev load state: buffer size mismatch: size %d, page %d/%d\n",
1149 (int)size, (int)iPage, (int)cPages));
1150}
1151
1152
1153static int vmmdevHGCMCallSaved (PVMMDEV pThis, VMMDevHGCMCall *pHGCMCall, RTGCPHYS GCPhys, uint32_t cbHGCMCall, bool f64Bits, bool *pfHGCMCalled, VBOXHGCMCMD *pSavedCmd, VBOXHGCMCMD **ppCmd)
1154{
1155 int rc = VINF_SUCCESS;
1156
1157 Log(("vmmdevHGCMCallSaved: client id = %d, function = %d, %s bit\n", pHGCMCall->u32ClientID, pHGCMCall->u32Function, f64Bits? "64": "32"));
1158
1159 /* Compute size and allocate memory block to hold:
1160 * struct VBOXHGCMCMD
1161 * VBOXHGCMSVCPARM[cParms]
1162 * memory buffers for pointer parameters.
1163 */
1164
1165 uint32_t cParms = pHGCMCall->cParms;
1166
1167 Log(("vmmdevHGCMCall: cParms = %d\n", cParms));
1168
1169 /*
1170 * Sane upper limit.
1171 */
1172 if (cParms > VMMDEV_MAX_HGCM_PARMS)
1173 {
1174 static int s_cRelWarn;
1175 if (s_cRelWarn < 50)
1176 {
1177 s_cRelWarn++;
1178 LogRel(("VMMDev: request packet with too many parameters (%d). Refusing operation.\n", cParms));
1179 }
1180 return VERR_INVALID_PARAMETER;
1181 }
1182
1183 /*
1184 * Compute size of required memory buffer.
1185 */
1186
1187 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + cParms * sizeof (VBOXHGCMSVCPARM);
1188
1189 uint32_t i;
1190
1191 int32_t cLinPtrs = 0;
1192 int32_t cLinPtrPages = 0;
1193
1194 if (f64Bits)
1195 {
1196#ifdef VBOX_WITH_64_BITS_GUESTS
1197 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
1198#else
1199 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1200 AssertFailed (); /* This code should not be called in this case */
1201#endif /* VBOX_WITH_64_BITS_GUESTS */
1202
1203 /* Look for pointer parameters, which require a host buffer. */
1204 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
1205 {
1206 switch (pGuestParm->type)
1207 {
1208 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1209 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1210 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1211 {
1212 if (pGuestParm->u.Pointer.size > 0)
1213 {
1214 /* Only pointers with some actual data are counted. */
1215 cbCmdSize += pGuestParm->u.Pointer.size;
1216
1217 cLinPtrs++;
1218 /* Take the offset into the current page also into account! */
1219 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
1220 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
1221 }
1222
1223 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
1224 } break;
1225
1226 case VMMDevHGCMParmType_PageList:
1227 {
1228 cbCmdSize += pGuestParm->u.PageList.size;
1229 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
1230 } break;
1231
1232 case VMMDevHGCMParmType_32bit:
1233 case VMMDevHGCMParmType_64bit:
1234 {
1235 } break;
1236
1237 default:
1238 case VMMDevHGCMParmType_PhysAddr:
1239 {
1240 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
1241 rc = VERR_INVALID_PARAMETER;
1242 break;
1243 }
1244 }
1245 }
1246 }
1247 else
1248 {
1249#ifdef VBOX_WITH_64_BITS_GUESTS
1250 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
1251#else
1252 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1253#endif /* VBOX_WITH_64_BITS_GUESTS */
1254
1255 /* Look for pointer parameters, which require a host buffer. */
1256 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++)
1257 {
1258 switch (pGuestParm->type)
1259 {
1260 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1261 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1262 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1263 {
1264 if (pGuestParm->u.Pointer.size > 0)
1265 {
1266 /* Only pointers with some actual data are counted. */
1267 cbCmdSize += pGuestParm->u.Pointer.size;
1268
1269 cLinPtrs++;
1270 /* Take the offset into the current page also into account! */
1271 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
1272 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
1273 }
1274
1275 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
1276 } break;
1277
1278 case VMMDevHGCMParmType_PageList:
1279 {
1280 cbCmdSize += pGuestParm->u.PageList.size;
1281 Log(("vmmdevHGCMCall: pagelist size = %d\n", pGuestParm->u.PageList.size));
1282 } break;
1283
1284 case VMMDevHGCMParmType_32bit:
1285 case VMMDevHGCMParmType_64bit:
1286 {
1287 } break;
1288
1289 default:
1290 {
1291 AssertMsgFailed(("vmmdevHGCMCall: invalid parameter type %x\n", pGuestParm->type));
1292 rc = VERR_INVALID_PARAMETER;
1293 break;
1294 }
1295 }
1296 }
1297 }
1298
1299 if (RT_FAILURE (rc))
1300 {
1301 return rc;
1302 }
1303
1304 if ( pSavedCmd->cLinPtrs != cLinPtrs
1305 || pSavedCmd->cLinPtrPages != cLinPtrPages)
1306 {
1307 LogRel(("VMMDev: invalid saved command ptrs: %d/%d, pages %d/%d\n",
1308 pSavedCmd->cLinPtrs, cLinPtrs, pSavedCmd->cLinPtrPages, cLinPtrPages));
1309 AssertFailed();
1310 return VERR_INVALID_PARAMETER;
1311 }
1312
1313 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (cbCmdSize);
1314
1315 if (pCmd == NULL)
1316 {
1317 return VERR_NO_MEMORY;
1318 }
1319
1320 vmmdevRestoreSavedCommand(pCmd, pSavedCmd);
1321 *ppCmd = pCmd;
1322
1323 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, pHGCMCall->header.header.size, VBOXHGCMCMDTYPE_CALL);
1324
1325 /* Process parameters, changing them to host context pointers for easy
1326 * processing by connector. Guest must insure that the pointed data is actually
1327 * in the guest RAM and remains locked there for entire request processing.
1328 */
1329
1330 if (cParms != 0)
1331 {
1332 /* Compute addresses of host parms array and first memory buffer. */
1333 VBOXHGCMSVCPARM *pHostParm = (VBOXHGCMSVCPARM *)((uint8_t *)pCmd + sizeof (struct VBOXHGCMCMD));
1334
1335 uint8_t *pu8Buf = (uint8_t *)pHostParm + cParms * sizeof (VBOXHGCMSVCPARM);
1336
1337 pCmd->paHostParms = pHostParm;
1338 pCmd->cHostParms = cParms;
1339
1340 uint32_t iParm;
1341 int iLinPtr = 0;
1342
1343 if (f64Bits)
1344 {
1345#ifdef VBOX_WITH_64_BITS_GUESTS
1346 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
1347#else
1348 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1349 AssertFailed (); /* This code should not be called in this case */
1350#endif /* VBOX_WITH_64_BITS_GUESTS */
1351
1352 for (iParm = 0; iParm < cParms && RT_SUCCESS(rc); iParm++, pGuestParm++, pHostParm++)
1353 {
1354 switch (pGuestParm->type)
1355 {
1356 case VMMDevHGCMParmType_32bit:
1357 {
1358 uint32_t u32 = pGuestParm->u.value32;
1359
1360 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
1361 pHostParm->u.uint32 = u32;
1362
1363 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
1364 break;
1365 }
1366
1367 case VMMDevHGCMParmType_64bit:
1368 {
1369 uint64_t u64 = pGuestParm->u.value64;
1370
1371 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
1372 pHostParm->u.uint64 = u64;
1373
1374 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
1375 break;
1376 }
1377
1378 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1379 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1380 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1381 {
1382 uint32_t size = pGuestParm->u.Pointer.size;
1383
1384 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1385 pHostParm->u.pointer.size = size;
1386
1387 /* Copy guest data to an allocated buffer, so
1388 * services can use the data.
1389 */
1390
1391 if (size == 0)
1392 {
1393 pHostParm->u.pointer.addr = NULL;
1394 }
1395 else
1396 {
1397 /* The saved command already have the page list in pCmd->paLinPtrs.
1398 * Read data from guest pages.
1399 */
1400 /* Don't overdo it */
1401 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
1402 {
1403 if ( iLinPtr >= pCmd->cLinPtrs
1404 || pCmd->paLinPtrs[iLinPtr].iParm != iParm)
1405 {
1406 logRelLoadStatePointerIndexMismatch (iParm, pCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pCmd->cLinPtrs);
1407 rc = VERR_INVALID_PARAMETER;
1408 }
1409 else
1410 {
1411 VBOXHGCMLINPTR *pLinPtr = &pCmd->paLinPtrs[iLinPtr];
1412
1413 uint32_t iPage;
1414 uint32_t offPage = pLinPtr->offFirstPage;
1415 size_t cbRemaining = size;
1416 uint8_t *pu8Dst = pu8Buf;
1417 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
1418 {
1419 if (cbRemaining == 0)
1420 {
1421 logRelLoadStateBufferSizeMismatch (size, iPage, pLinPtr->cPages);
1422 break;
1423 }
1424
1425 size_t cbChunk = PAGE_SIZE - offPage;
1426
1427 if (cbChunk > cbRemaining)
1428 {
1429 cbChunk = cbRemaining;
1430 }
1431
1432 rc = PDMDevHlpPhysRead(pThis->pDevIns,
1433 pLinPtr->paPages[iPage] + offPage,
1434 pu8Dst, cbChunk);
1435
1436 AssertRCBreak(rc);
1437
1438 offPage = 0; /* A next page is read from 0 offset. */
1439 cbRemaining -= cbChunk;
1440 pu8Dst += cbChunk;
1441 }
1442 }
1443 }
1444 else
1445 rc = VINF_SUCCESS;
1446
1447 if (RT_SUCCESS(rc))
1448 {
1449 pHostParm->u.pointer.addr = pu8Buf;
1450 pu8Buf += size;
1451
1452 iLinPtr++;
1453 }
1454 }
1455
1456 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n",
1457 pGuestParm->u.Pointer.u.linearAddr, rc));
1458 break;
1459 }
1460
1461 case VMMDevHGCMParmType_PageList:
1462 {
1463 uint32_t size = pGuestParm->u.PageList.size;
1464
1465 /* Check that the page list info is within the request. */
1466 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
1467 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
1468 {
1469 rc = VERR_INVALID_PARAMETER;
1470 break;
1471 }
1472
1473 /* At least the structure is within. */
1474 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
1475
1476 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
1477
1478 if ( pPageListInfo->cPages == 0
1479 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
1480 {
1481 rc = VERR_INVALID_PARAMETER;
1482 break;
1483 }
1484
1485 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1486 pHostParm->u.pointer.size = size;
1487
1488 /* Copy guest data to an allocated buffer, so
1489 * services can use the data.
1490 */
1491
1492 if (size == 0)
1493 {
1494 pHostParm->u.pointer.addr = NULL;
1495 }
1496 else
1497 {
1498 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
1499 {
1500 /* Copy pages to the pcBuf[size]. */
1501 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pu8Buf, size, pPageListInfo);
1502 }
1503 else
1504 rc = VINF_SUCCESS;
1505
1506 if (RT_SUCCESS(rc))
1507 {
1508 pHostParm->u.pointer.addr = pu8Buf;
1509 pu8Buf += size;
1510 }
1511 }
1512
1513 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
1514 break;
1515 }
1516
1517 /* just to shut up gcc */
1518 default:
1519 AssertFailed();
1520 break;
1521 }
1522 }
1523 }
1524 else
1525 {
1526#ifdef VBOX_WITH_64_BITS_GUESTS
1527 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
1528#else
1529 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
1530#endif /* VBOX_WITH_64_BITS_GUESTS */
1531
1532 for (iParm = 0; iParm < cParms && RT_SUCCESS(rc); iParm++, pGuestParm++, pHostParm++)
1533 {
1534 switch (pGuestParm->type)
1535 {
1536 case VMMDevHGCMParmType_32bit:
1537 {
1538 uint32_t u32 = pGuestParm->u.value32;
1539
1540 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
1541 pHostParm->u.uint32 = u32;
1542
1543 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
1544 break;
1545 }
1546
1547 case VMMDevHGCMParmType_64bit:
1548 {
1549 uint64_t u64 = pGuestParm->u.value64;
1550
1551 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
1552 pHostParm->u.uint64 = u64;
1553
1554 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
1555 break;
1556 }
1557
1558 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1559 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1560 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1561 {
1562 uint32_t size = pGuestParm->u.Pointer.size;
1563
1564 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1565 pHostParm->u.pointer.size = size;
1566
1567 /* Copy guest data to an allocated buffer, so
1568 * services can use the data.
1569 */
1570
1571 if (size == 0)
1572 {
1573 pHostParm->u.pointer.addr = NULL;
1574 }
1575 else
1576 {
1577 /* The saved command already have the page list in pCmd->paLinPtrs.
1578 * Read data from guest pages.
1579 */
1580 /* Don't overdo it */
1581 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
1582 {
1583 if ( iLinPtr >= pCmd->cLinPtrs
1584 || pCmd->paLinPtrs[iLinPtr].iParm != iParm)
1585 {
1586 logRelLoadStatePointerIndexMismatch (iParm, pCmd->paLinPtrs[iLinPtr].iParm, iLinPtr, pCmd->cLinPtrs);
1587 rc = VERR_INVALID_PARAMETER;
1588 }
1589 else
1590 {
1591 VBOXHGCMLINPTR *pLinPtr = &pCmd->paLinPtrs[iLinPtr];
1592
1593 uint32_t iPage;
1594 uint32_t offPage = pLinPtr->offFirstPage;
1595 size_t cbRemaining = size;
1596 uint8_t *pu8Dst = pu8Buf;
1597 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
1598 {
1599 if (cbRemaining == 0)
1600 {
1601 logRelLoadStateBufferSizeMismatch (size, iPage, pLinPtr->cPages);
1602 break;
1603 }
1604
1605 size_t cbChunk = PAGE_SIZE - offPage;
1606
1607 if (cbChunk > cbRemaining)
1608 {
1609 cbChunk = cbRemaining;
1610 }
1611
1612 rc = PDMDevHlpPhysRead(pThis->pDevIns,
1613 pLinPtr->paPages[iPage] + offPage,
1614 pu8Dst, cbChunk);
1615
1616 AssertRCBreak(rc);
1617
1618 offPage = 0; /* A next page is read from 0 offset. */
1619 cbRemaining -= cbChunk;
1620 pu8Dst += cbChunk;
1621 }
1622 }
1623 }
1624 else
1625 rc = VINF_SUCCESS;
1626
1627 if (RT_SUCCESS(rc))
1628 {
1629 pHostParm->u.pointer.addr = pu8Buf;
1630 pu8Buf += size;
1631
1632 iLinPtr++;
1633 }
1634 }
1635
1636 Log(("vmmdevHGCMCall: LinAddr guest parameter %RGv, rc = %Rrc\n",
1637 pGuestParm->u.Pointer.u.linearAddr, rc));
1638 break;
1639 }
1640
1641 case VMMDevHGCMParmType_PageList:
1642 {
1643 uint32_t size = pGuestParm->u.PageList.size;
1644
1645 /* Check that the page list info is within the request. */
1646 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
1647 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
1648 {
1649 rc = VERR_INVALID_PARAMETER;
1650 break;
1651 }
1652
1653 /* At least the structure is within. */
1654 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
1655
1656 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
1657
1658 if ( pPageListInfo->cPages == 0
1659 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
1660 {
1661 rc = VERR_INVALID_PARAMETER;
1662 break;
1663 }
1664
1665 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
1666 pHostParm->u.pointer.size = size;
1667
1668 /* Copy guest data to an allocated buffer, so
1669 * services can use the data.
1670 */
1671
1672 if (size == 0)
1673 {
1674 pHostParm->u.pointer.addr = NULL;
1675 }
1676 else
1677 {
1678 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_TO_HOST)
1679 {
1680 /* Copy pages to the pcBuf[size]. */
1681 rc = vmmdevHGCMPageListRead(pThis->pDevIns, pu8Buf, size, pPageListInfo);
1682 }
1683 else
1684 rc = VINF_SUCCESS;
1685
1686 if (RT_SUCCESS(rc))
1687 {
1688 pHostParm->u.pointer.addr = pu8Buf;
1689 pu8Buf += size;
1690 }
1691 }
1692
1693 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
1694 break;
1695 }
1696
1697 /* just to shut up gcc */
1698 default:
1699 AssertFailed();
1700 break;
1701 }
1702 }
1703 }
1704 }
1705
1706 if (RT_SUCCESS (rc))
1707 {
1708 /* Pass the function call to HGCM connector for actual processing */
1709 rc = pThis->pHGCMDrv->pfnCall (pThis->pHGCMDrv, pCmd, pHGCMCall->u32ClientID, pHGCMCall->u32Function, cParms, pCmd->paHostParms);
1710 if (RT_SUCCESS (rc))
1711 {
1712 *pfHGCMCalled = true;
1713 }
1714 }
1715
1716 return rc;
1717}
1718
1719/**
1720 * VMMDevReq_HGCMCancel worker.
1721 *
1722 * @thread EMT
1723 */
1724int vmmdevHGCMCancel (PVMMDEV pThis, VMMDevHGCMCancel *pHGCMCancel, RTGCPHYS GCPhys)
1725{
1726 NOREF(pHGCMCancel);
1727 int rc = vmmdevHGCMCancel2(pThis, GCPhys);
1728 return rc == VERR_NOT_FOUND ? VERR_INVALID_PARAMETER : rc;
1729}
1730
1731/**
1732 * VMMDevReq_HGCMCancel2 worker.
1733 *
1734 * @retval VINF_SUCCESS on success.
1735 * @retval VERR_NOT_FOUND if the request was not found.
1736 * @retval VERR_INVALID_PARAMETER if the request address is invalid.
1737 *
1738 * @param pThis The VMMDev instance data.
1739 * @param GCPhys The address of the request that should be cancelled.
1740 *
1741 * @thread EMT
1742 */
1743int vmmdevHGCMCancel2 (PVMMDEV pThis, RTGCPHYS GCPhys)
1744{
1745 if ( GCPhys == 0
1746 || GCPhys == NIL_RTGCPHYS
1747 || GCPhys == NIL_RTGCPHYS32)
1748 {
1749 Log(("vmmdevHGCMCancel2: GCPhys=%#x\n", GCPhys));
1750 return VERR_INVALID_PARAMETER;
1751 }
1752
1753 /*
1754 * Locate the command and cancel it while under the protection of
1755 * the lock. hgcmCompletedWorker makes assumptions about this.
1756 */
1757 int rc = vmmdevHGCMCmdListLock (pThis);
1758 AssertRCReturn(rc, rc);
1759
1760 PVBOXHGCMCMD pCmd = vmmdevHGCMFindCommandLocked (pThis, GCPhys);
1761 if (pCmd)
1762 {
1763 pCmd->fCancelled = true;
1764 Log(("vmmdevHGCMCancel2: Cancelled pCmd=%p / GCPhys=%#x\n", pCmd, GCPhys));
1765 }
1766 else
1767 rc = VERR_NOT_FOUND;
1768
1769 vmmdevHGCMCmdListUnlock (pThis);
1770 return rc;
1771}
1772
1773static int vmmdevHGCMCmdVerify (PVBOXHGCMCMD pCmd, VMMDevHGCMRequestHeader *pHeader)
1774{
1775 switch (pCmd->enmCmdType)
1776 {
1777 case VBOXHGCMCMDTYPE_CONNECT:
1778 if ( pHeader->header.requestType == VMMDevReq_HGCMConnect
1779 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1780 break;
1781
1782 case VBOXHGCMCMDTYPE_DISCONNECT:
1783 if ( pHeader->header.requestType == VMMDevReq_HGCMDisconnect
1784 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1785 break;
1786
1787 case VBOXHGCMCMDTYPE_CALL:
1788#ifdef VBOX_WITH_64_BITS_GUESTS
1789 if ( pHeader->header.requestType == VMMDevReq_HGCMCall32
1790 || pHeader->header.requestType == VMMDevReq_HGCMCall64
1791 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1792#else
1793 if ( pHeader->header.requestType == VMMDevReq_HGCMCall
1794 || pHeader->header.requestType == VMMDevReq_HGCMCancel) return VINF_SUCCESS;
1795#endif /* VBOX_WITH_64_BITS_GUESTS */
1796
1797 break;
1798
1799 default:
1800 AssertFailed ();
1801 }
1802
1803 LogRel(("VMMDEV: Invalid HGCM command: pCmd->enmCmdType = 0x%08X, pHeader->header.requestType = 0x%08X\n",
1804 pCmd->enmCmdType, pHeader->header.requestType));
1805 return VERR_INVALID_PARAMETER;
1806}
1807
1808#ifdef VBOX_WITH_64_BITS_GUESTS
1809static int vmmdevHGCMParmVerify64(HGCMFunctionParameter64 *pGuestParm, VBOXHGCMSVCPARM *pHostParm)
1810{
1811 int rc = VERR_INVALID_PARAMETER;
1812
1813 switch (pGuestParm->type)
1814 {
1815 case VMMDevHGCMParmType_32bit:
1816 if (pHostParm->type == VBOX_HGCM_SVC_PARM_32BIT)
1817 rc = VINF_SUCCESS;
1818 break;
1819
1820 case VMMDevHGCMParmType_64bit:
1821 if (pHostParm->type == VBOX_HGCM_SVC_PARM_64BIT)
1822 rc = VINF_SUCCESS;
1823 break;
1824
1825 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1826 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1827 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1828 if ( pHostParm->type == VBOX_HGCM_SVC_PARM_PTR
1829 && pGuestParm->u.Pointer.size >= pHostParm->u.pointer.size)
1830 rc = VINF_SUCCESS;
1831 break;
1832
1833 case VMMDevHGCMParmType_PageList:
1834 if ( pHostParm->type == VBOX_HGCM_SVC_PARM_PTR
1835 && pGuestParm->u.PageList.size >= pHostParm->u.pointer.size)
1836 rc = VINF_SUCCESS;
1837 break;
1838
1839 default:
1840 AssertLogRelMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
1841 break;
1842 }
1843
1844 return rc;
1845}
1846#endif /* VBOX_WITH_64_BITS_GUESTS */
1847
1848#ifdef VBOX_WITH_64_BITS_GUESTS
1849static int vmmdevHGCMParmVerify32(HGCMFunctionParameter32 *pGuestParm, VBOXHGCMSVCPARM *pHostParm)
1850#else
1851static int vmmdevHGCMParmVerify32(HGCMFunctionParameter *pGuestParm, VBOXHGCMSVCPARM *pHostParm)
1852#endif
1853{
1854 int rc = VERR_INVALID_PARAMETER;
1855
1856 switch (pGuestParm->type)
1857 {
1858 case VMMDevHGCMParmType_32bit:
1859 if (pHostParm->type == VBOX_HGCM_SVC_PARM_32BIT)
1860 rc = VINF_SUCCESS;
1861 break;
1862
1863 case VMMDevHGCMParmType_64bit:
1864 if (pHostParm->type == VBOX_HGCM_SVC_PARM_64BIT)
1865 rc = VINF_SUCCESS;
1866 break;
1867
1868 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
1869 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
1870 case VMMDevHGCMParmType_LinAddr: /* In & Out */
1871 if ( pHostParm->type == VBOX_HGCM_SVC_PARM_PTR
1872 && pGuestParm->u.Pointer.size >= pHostParm->u.pointer.size)
1873 rc = VINF_SUCCESS;
1874 break;
1875
1876 case VMMDevHGCMParmType_PageList:
1877 if ( pHostParm->type == VBOX_HGCM_SVC_PARM_PTR
1878 && pGuestParm->u.PageList.size >= pHostParm->u.pointer.size)
1879 rc = VINF_SUCCESS;
1880 break;
1881
1882 default:
1883 AssertLogRelMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
1884 break;
1885 }
1886
1887 return rc;
1888}
1889
1890DECLCALLBACK(void) hgcmCompletedWorker (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
1891{
1892 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
1893#ifdef VBOX_WITH_DTRACE
1894 uint32_t idFunction = 0;
1895 uint32_t idClient = 0;
1896#endif
1897
1898 int rc = VINF_SUCCESS;
1899
1900 if (result == VINF_HGCM_SAVE_STATE)
1901 {
1902 /* If the completion routine was called because HGCM saves its state,
1903 * then currently nothing to be done here. The pCmd stays in the list
1904 * and will be saved later when the VMMDev state will be saved.
1905 *
1906 * It it assumed that VMMDev saves state after the HGCM services,
1907 * and, therefore, VBOXHGCMCMD structures are not removed by
1908 * vmmdevHGCMSaveState from the list, while HGCM uses them.
1909 */
1910 LogFlowFunc(("VINF_HGCM_SAVE_STATE for command %p\n", pCmd));
1911 return;
1912 }
1913
1914 /*
1915 * The cancellation protocol requires us to remove the command here
1916 * and then check the flag. Cancelled commands must not be written
1917 * back to guest memory.
1918 */
1919 VBOXDD_HGCMCALL_COMPLETED_EMT(pCmd, result);
1920 vmmdevHGCMRemoveCommand (pThis, pCmd);
1921
1922 if (pCmd->fCancelled || pCmd->fSaved)
1923 {
1924 LogFlowFunc(("A cancelled command %p: %d %d\n", pCmd, pCmd->fCancelled, pCmd->fSaved));
1925 }
1926 else
1927 {
1928 /* Preallocated block for requests which have up to 8 parameters (most of requests). */
1929#ifdef VBOX_WITH_64_BITS_GUESTS
1930 uint8_t au8Prealloc[sizeof (VMMDevHGCMCall) + 8 * sizeof (HGCMFunctionParameter64)];
1931#else
1932 uint8_t au8Prealloc[sizeof (VMMDevHGCMCall) + 8 * sizeof (HGCMFunctionParameter)];
1933#endif /* VBOX_WITH_64_BITS_GUESTS */
1934
1935 VMMDevHGCMRequestHeader *pHeader;
1936
1937 if (pCmd->cbSize <= sizeof (au8Prealloc))
1938 {
1939 pHeader = (VMMDevHGCMRequestHeader *)&au8Prealloc[0];
1940 }
1941 else
1942 {
1943 pHeader = (VMMDevHGCMRequestHeader *)RTMemAlloc (pCmd->cbSize);
1944 if (pHeader == NULL)
1945 {
1946 LogRel(("VMMDev: Failed to allocate %u bytes for HGCM request completion!!!\n", pCmd->cbSize));
1947
1948 /* Free it. The command have to be excluded from list of active commands anyway. */
1949 RTMemFree (pCmd);
1950 return;
1951 }
1952 }
1953
1954 /*
1955 * Enter and leave the critical section here so we make sure
1956 * vmmdevRequestHandler has completed before we read & write
1957 * the request. (This isn't 100% optimal, but it solves the
1958 * 3.0 blocker.)
1959 */
1960 /** @todo It would be faster if this interface would use MMIO2 memory and we
1961 * didn't have to mess around with PDMDevHlpPhysRead/Write. We're
1962 * reading the header 3 times now and writing the request back twice. */
1963
1964 PDMCritSectEnter(&pThis->CritSect, VERR_SEM_BUSY);
1965 PDMCritSectLeave(&pThis->CritSect);
1966
1967 PDMDevHlpPhysRead(pThis->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbSize);
1968
1969 /* Setup return codes. */
1970 pHeader->result = result;
1971
1972 /* Verify the request type. */
1973 rc = vmmdevHGCMCmdVerify (pCmd, pHeader);
1974
1975 if (RT_SUCCESS (rc))
1976 {
1977 /* Update parameters and data buffers. */
1978
1979 switch (pHeader->header.requestType)
1980 {
1981#ifdef VBOX_WITH_64_BITS_GUESTS
1982 case VMMDevReq_HGCMCall64:
1983 {
1984 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
1985
1986 uint32_t cParms = pHGCMCall->cParms;
1987 if (cParms != pCmd->cHostParms)
1988 rc = VERR_INVALID_PARAMETER;
1989
1990 VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms;
1991
1992 uint32_t i;
1993 uint32_t iLinPtr = 0;
1994
1995 HGCMFunctionParameter64 *pGuestParm = VMMDEV_HGCM_CALL_PARMS64(pHGCMCall);
1996
1997 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
1998 {
1999 rc = vmmdevHGCMParmVerify64(pGuestParm, pHostParm);
2000 if (RT_FAILURE(rc))
2001 break;
2002
2003 switch (pGuestParm->type)
2004 {
2005 case VMMDevHGCMParmType_32bit:
2006 {
2007 pGuestParm->u.value32 = pHostParm->u.uint32;
2008 } break;
2009
2010 case VMMDevHGCMParmType_64bit:
2011 {
2012 pGuestParm->u.value64 = pHostParm->u.uint64;
2013 } break;
2014
2015 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
2016 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
2017 case VMMDevHGCMParmType_LinAddr: /* In & Out */
2018 {
2019 /* Copy buffer back to guest memory. */
2020 uint32_t size = pGuestParm->u.Pointer.size;
2021
2022 if (size > 0)
2023 {
2024 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
2025 {
2026 /* Use the saved page list to write data back to the guest RAM. */
2027 rc = vmmdevHGCMWriteLinPtr (pThis->pDevIns, i, pHostParm->u.pointer.addr,
2028 size, iLinPtr, pCmd->paLinPtrs);
2029 }
2030
2031 /* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
2032 iLinPtr++;
2033 }
2034 } break;
2035
2036 case VMMDevHGCMParmType_PageList:
2037 {
2038 uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
2039
2040 uint32_t size = pGuestParm->u.PageList.size;
2041
2042 /* Check that the page list info is within the request. */
2043 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
2044 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
2045 {
2046 rc = VERR_INVALID_PARAMETER;
2047 break;
2048 }
2049
2050 /* At least the structure is within. */
2051 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
2052
2053 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
2054
2055 if ( pPageListInfo->cPages == 0
2056 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
2057 {
2058 rc = VERR_INVALID_PARAMETER;
2059 break;
2060 }
2061
2062 if (size > 0)
2063 {
2064 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
2065 {
2066 /* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
2067 rc = vmmdevHGCMPageListWrite(pThis->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
2068 }
2069 else
2070 rc = VINF_SUCCESS;
2071 }
2072
2073 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
2074 } break;
2075
2076 default:
2077 {
2078 /* This indicates that the guest request memory was corrupted. */
2079 rc = VERR_INVALID_PARAMETER;
2080 break;
2081 }
2082 }
2083 }
2084# ifdef VBOX_WITH_DTRACE
2085 idFunction = pHGCMCall->u32Function;
2086 idClient = pHGCMCall->u32ClientID;
2087# endif
2088 break;
2089 }
2090
2091 case VMMDevReq_HGCMCall32:
2092 {
2093 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
2094
2095 uint32_t cParms = pHGCMCall->cParms;
2096 if (cParms != pCmd->cHostParms)
2097 rc = VERR_INVALID_PARAMETER;
2098
2099 VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms;
2100
2101 uint32_t i;
2102 uint32_t iLinPtr = 0;
2103
2104 HGCMFunctionParameter32 *pGuestParm = VMMDEV_HGCM_CALL_PARMS32(pHGCMCall);
2105
2106 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
2107 {
2108 rc = vmmdevHGCMParmVerify32(pGuestParm, pHostParm);
2109 if (RT_FAILURE(rc))
2110 break;
2111
2112 switch (pGuestParm->type)
2113 {
2114 case VMMDevHGCMParmType_32bit:
2115 {
2116 pGuestParm->u.value32 = pHostParm->u.uint32;
2117 } break;
2118
2119 case VMMDevHGCMParmType_64bit:
2120 {
2121 pGuestParm->u.value64 = pHostParm->u.uint64;
2122 } break;
2123
2124 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
2125 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
2126 case VMMDevHGCMParmType_LinAddr: /* In & Out */
2127 {
2128 /* Copy buffer back to guest memory. */
2129 uint32_t size = pGuestParm->u.Pointer.size;
2130
2131 if (size > 0)
2132 {
2133 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
2134 {
2135 /* Use the saved page list to write data back to the guest RAM. */
2136 rc = vmmdevHGCMWriteLinPtr (pThis->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr, pCmd->paLinPtrs);
2137 }
2138
2139 /* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
2140 iLinPtr++;
2141 }
2142 } break;
2143
2144 case VMMDevHGCMParmType_PageList:
2145 {
2146 uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
2147
2148 uint32_t size = pGuestParm->u.PageList.size;
2149
2150 /* Check that the page list info is within the request. */
2151 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
2152 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
2153 {
2154 rc = VERR_INVALID_PARAMETER;
2155 break;
2156 }
2157
2158 /* At least the structure is within. */
2159 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
2160
2161 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
2162
2163 if ( pPageListInfo->cPages == 0
2164 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
2165 {
2166 rc = VERR_INVALID_PARAMETER;
2167 break;
2168 }
2169
2170 if (size > 0)
2171 {
2172 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
2173 {
2174 /* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
2175 rc = vmmdevHGCMPageListWrite(pThis->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
2176 }
2177 else
2178 rc = VINF_SUCCESS;
2179 }
2180
2181 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
2182 } break;
2183
2184 default:
2185 {
2186 /* This indicates that the guest request memory was corrupted. */
2187 rc = VERR_INVALID_PARAMETER;
2188 break;
2189 }
2190 }
2191 }
2192# ifdef VBOX_WITH_DTRACE
2193 idFunction = pHGCMCall->u32Function;
2194 idClient = pHGCMCall->u32ClientID;
2195# endif
2196 break;
2197 }
2198#else
2199 case VMMDevReq_HGCMCall:
2200 {
2201 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
2202
2203 uint32_t cParms = pHGCMCall->cParms;
2204 if (cParms != pCmd->cHostParms)
2205 rc = VERR_INVALID_PARAMETER;
2206
2207 VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms;
2208
2209 uint32_t i;
2210 uint32_t iLinPtr = 0;
2211
2212 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
2213
2214 for (i = 0; i < cParms && RT_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
2215 {
2216 rc = vmmdevHGCMParmVerify32(pGuestParm, pHostParm);
2217 if (RT_FAILURE(rc))
2218 break;
2219
2220 switch (pGuestParm->type)
2221 {
2222 case VMMDevHGCMParmType_32bit:
2223 {
2224 pGuestParm->u.value32 = pHostParm->u.uint32;
2225 } break;
2226
2227 case VMMDevHGCMParmType_64bit:
2228 {
2229 pGuestParm->u.value64 = pHostParm->u.uint64;
2230 } break;
2231
2232 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
2233 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
2234 case VMMDevHGCMParmType_LinAddr: /* In & Out */
2235 {
2236 /* Copy buffer back to guest memory. */
2237 uint32_t size = pGuestParm->u.Pointer.size;
2238
2239 if (size > 0)
2240 {
2241 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
2242 {
2243 /* Use the saved page list to write data back to the guest RAM. */
2244 rc = vmmdevHGCMWriteLinPtr (pThis->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr, pCmd->paLinPtrs);
2245 }
2246
2247 /* All linptrs with size > 0 were saved. Advance the index to the next linptr. */
2248 iLinPtr++;
2249 }
2250 } break;
2251
2252 case VMMDevHGCMParmType_PageList:
2253 {
2254 uint32_t cbHGCMCall = pCmd->cbSize; /* Size of the request. */
2255
2256 uint32_t size = pGuestParm->u.PageList.size;
2257
2258 /* Check that the page list info is within the request. */
2259 if ( cbHGCMCall < sizeof (HGCMPageListInfo)
2260 || pGuestParm->u.PageList.offset > cbHGCMCall - sizeof (HGCMPageListInfo))
2261 {
2262 rc = VERR_INVALID_PARAMETER;
2263 break;
2264 }
2265
2266 /* At least the structure is within. */
2267 HGCMPageListInfo *pPageListInfo = (HGCMPageListInfo *)((uint8_t *)pHGCMCall + pGuestParm->u.PageList.offset);
2268
2269 uint32_t cbPageListInfo = sizeof (HGCMPageListInfo) + (pPageListInfo->cPages - 1) * sizeof (pPageListInfo->aPages[0]);
2270
2271 if ( pPageListInfo->cPages == 0
2272 || cbHGCMCall < pGuestParm->u.PageList.offset + cbPageListInfo)
2273 {
2274 rc = VERR_INVALID_PARAMETER;
2275 break;
2276 }
2277
2278 if (size > 0)
2279 {
2280 if (pPageListInfo->flags & VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST)
2281 {
2282 /* Copy pHostParm->u.pointer.addr[pHostParm->u.pointer.size] to pages. */
2283 rc = vmmdevHGCMPageListWrite(pThis->pDevIns, pPageListInfo, pHostParm->u.pointer.addr, size);
2284 }
2285 else
2286 rc = VINF_SUCCESS;
2287 }
2288
2289 Log(("vmmdevHGCMCall: PageList guest parameter rc = %Rrc\n", rc));
2290 } break;
2291
2292 default:
2293 {
2294 /* This indicates that the guest request memory was corrupted. */
2295 rc = VERR_INVALID_PARAMETER;
2296 break;
2297 }
2298 }
2299 }
2300# ifdef VBOX_WITH_DTRACE
2301 idFunction = pHGCMCall->u32Function;
2302 idClient = pHGCMCall->u32ClientID;
2303# endif
2304 break;
2305 }
2306#endif /* VBOX_WITH_64_BITS_GUESTS */
2307 case VMMDevReq_HGCMConnect:
2308 {
2309 VMMDevHGCMConnect *pHGCMConnectCopy = (VMMDevHGCMConnect *)(pCmd+1);
2310
2311 /* save the client id in the guest request packet */
2312 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)pHeader;
2313 pHGCMConnect->u32ClientID = pHGCMConnectCopy->u32ClientID;
2314 break;
2315 }
2316
2317 default:
2318 /* make gcc happy */
2319 break;
2320 }
2321 }
2322
2323 if (RT_FAILURE(rc))
2324 {
2325 /* Command is wrong. Return HGCM error result to the guest. */
2326 pHeader->result = rc;
2327 }
2328
2329 /* Mark request as processed. */
2330 pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
2331
2332 /* Write back the request */
2333 PDMDevHlpPhysWrite(pThis->pDevIns, pCmd->GCPhys, pHeader, pCmd->cbSize);
2334
2335 /* Now, when the command was removed from the internal list, notify the guest. */
2336 VMMDevNotifyGuest (pThis, VMMDEV_EVENT_HGCM);
2337
2338 if ((uint8_t *)pHeader != &au8Prealloc[0])
2339 {
2340 /* Only if it was allocated from heap. */
2341 RTMemFree (pHeader);
2342 }
2343 }
2344
2345 /* Deallocate the command memory. */
2346 if (pCmd->paLinPtrs)
2347 {
2348 RTMemFree (pCmd->paLinPtrs);
2349 }
2350
2351 RTMemFree (pCmd);
2352
2353 VBOXDD_HGCMCALL_COMPLETED_DONE(pCmd, idFunction, idClient, result);
2354 return;
2355}
2356
2357DECLCALLBACK(void) hgcmCompleted (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
2358{
2359 PVMMDEV pThis = RT_FROM_MEMBER(pInterface, VMMDevState, IHGCMPort);
2360
2361 VBOXDD_HGCMCALL_COMPLETED_REQ(pCmd, result);
2362
2363/** @todo no longer necessary to forward to EMT, but it might be more
2364 * efficient...? */
2365 /* Not safe to execute asynchronously; forward to EMT */
2366 int rc = VMR3ReqCallVoidNoWait(PDMDevHlpGetVM(pThis->pDevIns), VMCPUID_ANY,
2367 (PFNRT)hgcmCompletedWorker, 3, pInterface, result, pCmd);
2368 AssertRC(rc);
2369}
2370
2371/** @thread EMT */
2372int vmmdevHGCMSaveState(PVMMDEV pThis, PSSMHANDLE pSSM)
2373{
2374 /* Save information about pending requests.
2375 * Only GCPtrs are of interest.
2376 */
2377 int rc = VINF_SUCCESS;
2378
2379 LogFlowFunc(("\n"));
2380
2381 /* Compute how many commands are pending. */
2382 uint32_t cCmds = 0;
2383
2384 PVBOXHGCMCMD pIter = pThis->pHGCMCmdList;
2385
2386 while (pIter)
2387 {
2388 LogFlowFunc (("pIter %p\n", pIter));
2389 cCmds++;
2390 pIter = pIter->pNext;
2391 }
2392
2393 LogFlowFunc(("cCmds = %d\n", cCmds));
2394
2395 /* Save number of commands. */
2396 rc = SSMR3PutU32(pSSM, cCmds);
2397 AssertRCReturn(rc, rc);
2398
2399 if (cCmds > 0)
2400 {
2401 pIter = pThis->pHGCMCmdList;
2402
2403 while (pIter)
2404 {
2405 PVBOXHGCMCMD pNext = pIter->pNext;
2406
2407 LogFlowFunc (("Saving %RGp, size %d\n", pIter->GCPhys, pIter->cbSize));
2408
2409 /* GC physical address of the guest request. */
2410 rc = SSMR3PutGCPhys(pSSM, pIter->GCPhys);
2411 AssertRCReturn(rc, rc);
2412
2413 /* Request packet size */
2414 rc = SSMR3PutU32(pSSM, pIter->cbSize);
2415 AssertRCReturn(rc, rc);
2416
2417 /*
2418 * Version 9+: save complete information about commands.
2419 */
2420
2421 /* The type of the command. */
2422 rc = SSMR3PutU32(pSSM, (uint32_t)pIter->enmCmdType);
2423 AssertRCReturn(rc, rc);
2424
2425 /* Whether the command was cancelled by the guest. */
2426 rc = SSMR3PutBool(pSSM, pIter->fCancelled);
2427 AssertRCReturn(rc, rc);
2428
2429 /* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */
2430 rc = SSMR3PutU32(pSSM, (uint32_t)pIter->cLinPtrs);
2431 AssertRCReturn(rc, rc);
2432
2433 if (pIter->cLinPtrs > 0)
2434 {
2435 /* How many pages for all linptrs in this command. */
2436 rc = SSMR3PutU32(pSSM, (uint32_t)pIter->cLinPtrPages);
2437 AssertRCReturn(rc, rc);
2438 }
2439
2440 int i;
2441 for (i = 0; i < pIter->cLinPtrs; i++)
2442 {
2443 /* Pointer to descriptions of linear pointers. */
2444 VBOXHGCMLINPTR *pLinPtr = &pIter->paLinPtrs[i];
2445
2446 /* Index of the parameter. */
2447 rc = SSMR3PutU32(pSSM, (uint32_t)pLinPtr->iParm);
2448 AssertRCReturn(rc, rc);
2449
2450 /* Offset in the first physical page of the region. */
2451 rc = SSMR3PutU32(pSSM, pLinPtr->offFirstPage);
2452 AssertRCReturn(rc, rc);
2453
2454 /* How many pages. */
2455 rc = SSMR3PutU32(pSSM, pLinPtr->cPages);
2456 AssertRCReturn(rc, rc);
2457
2458 uint32_t iPage;
2459 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
2460 {
2461 /* Array of the GC physical addresses for these pages.
2462 * It is assumed that the physical address of the locked resident
2463 * guest page does not change.
2464 */
2465 rc = SSMR3PutGCPhys(pSSM, pLinPtr->paPages[iPage]);
2466 AssertRCReturn(rc, rc);
2467 }
2468 }
2469
2470 /* A reserved field, will allow to extend saved data for a command. */
2471 rc = SSMR3PutU32(pSSM, 0);
2472 AssertRCReturn(rc, rc);
2473
2474 /* Mark the command as saved to make sure that it will not be
2475 * completed later by a still running host service.
2476 */
2477 pIter->fSaved = true;
2478 vmmdevHGCMRemoveCommand (pThis, pIter);
2479
2480 pIter = pNext;
2481 }
2482 }
2483
2484 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
2485 rc = SSMR3PutU32(pSSM, 0);
2486 AssertRCReturn(rc, rc);
2487
2488 return rc;
2489}
2490
2491/** @thread EMT(0) */
2492int vmmdevHGCMLoadState(PVMMDEV pThis, PSSMHANDLE pSSM, uint32_t uVersion)
2493{
2494 int rc = VINF_SUCCESS;
2495
2496 LogFlowFunc(("\n"));
2497
2498 /* Read how many commands were pending. */
2499 uint32_t cCmds = 0;
2500 rc = SSMR3GetU32(pSSM, &cCmds);
2501 AssertRCReturn(rc, rc);
2502
2503 LogFlowFunc(("cCmds = %d\n", cCmds));
2504
2505 if (uVersion < 9)
2506 {
2507 /* Only the guest physical address is saved. */
2508 while (cCmds--)
2509 {
2510 RTGCPHYS GCPhys;
2511 uint32_t cbSize;
2512
2513 rc = SSMR3GetGCPhys(pSSM, &GCPhys);
2514 AssertRCReturn(rc, rc);
2515
2516 rc = SSMR3GetU32(pSSM, &cbSize);
2517 AssertRCReturn(rc, rc);
2518
2519 LogFlowFunc (("Restoring %RGp size %x bytes\n", GCPhys, cbSize));
2520
2521 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (sizeof (struct VBOXHGCMCMD));
2522 AssertReturn(pCmd, VERR_NO_MEMORY);
2523
2524 pCmd->enmCmdType = VBOXHGCMCMDTYPE_LOADSTATE; /* This marks the "old" saved command. */
2525
2526 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, cbSize, VBOXHGCMCMDTYPE_LOADSTATE);
2527 }
2528 }
2529 else
2530 {
2531 /*
2532 * Version 9+: Load complete information about commands.
2533 */
2534 uint32_t u32;
2535 bool f;
2536
2537 while (cCmds--)
2538 {
2539 RTGCPHYS GCPhys;
2540 uint32_t cbSize;
2541
2542 /* GC physical address of the guest request. */
2543 rc = SSMR3GetGCPhys(pSSM, &GCPhys);
2544 AssertRCReturn(rc, rc);
2545
2546 /* The request packet size */
2547 rc = SSMR3GetU32(pSSM, &cbSize);
2548 AssertRCReturn(rc, rc);
2549
2550 LogFlowFunc (("Restoring %RGp size %x bytes\n", GCPhys, cbSize));
2551
2552 /* For uVersion <= 12, this was the size of entire command.
2553 * Now the size is recalculated in vmmdevHGCMLoadStateDone.
2554 */
2555 if (uVersion <= 12)
2556 {
2557 rc = SSMR3Skip(pSSM, sizeof (uint32_t));
2558 AssertRCReturn(rc, rc);
2559 }
2560
2561 /* Allocate only VBOXHGCMCMD structure. vmmdevHGCMLoadStateDone will reallocate the command
2562 * with additional space for parameters and for pointer/pagelists buffer.
2563 */
2564 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ (sizeof (VBOXHGCMCMD));
2565 AssertReturn(pCmd, VERR_NO_MEMORY);
2566
2567 /* The type of the command. */
2568 rc = SSMR3GetU32(pSSM, &u32);
2569 AssertRCReturn(rc, rc);
2570 pCmd->enmCmdType = (VBOXHGCMCMDTYPE)u32;
2571
2572 /* Whether the command was cancelled by the guest. */
2573 rc = SSMR3GetBool(pSSM, &f);
2574 AssertRCReturn(rc, rc);
2575 pCmd->fCancelled = f;
2576
2577 /* Linear pointer parameters information. How many pointers. Always 0 if not a call command. */
2578 rc = SSMR3GetU32(pSSM, &u32);
2579 AssertRCReturn(rc, rc);
2580 pCmd->cLinPtrs = u32;
2581
2582 if (pCmd->cLinPtrs > 0)
2583 {
2584 /* How many pages for all linptrs in this command. */
2585 rc = SSMR3GetU32(pSSM, &u32);
2586 AssertRCReturn(rc, rc);
2587 pCmd->cLinPtrPages = u32;
2588
2589 pCmd->paLinPtrs = (VBOXHGCMLINPTR *)RTMemAllocZ ( sizeof (VBOXHGCMLINPTR) * pCmd->cLinPtrs
2590 + sizeof (RTGCPHYS) * pCmd->cLinPtrPages);
2591 AssertReturn(pCmd->paLinPtrs, VERR_NO_MEMORY);
2592
2593 RTGCPHYS *pPages = (RTGCPHYS *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) * pCmd->cLinPtrs);
2594 int cPages = 0;
2595
2596 int i;
2597 for (i = 0; i < pCmd->cLinPtrs; i++)
2598 {
2599 /* Pointer to descriptions of linear pointers. */
2600 VBOXHGCMLINPTR *pLinPtr = &pCmd->paLinPtrs[i];
2601
2602 pLinPtr->paPages = pPages;
2603
2604 /* Index of the parameter. */
2605 rc = SSMR3GetU32(pSSM, &u32);
2606 AssertRCReturn(rc, rc);
2607 pLinPtr->iParm = u32;
2608
2609 /* Offset in the first physical page of the region. */
2610 rc = SSMR3GetU32(pSSM, &u32);
2611 AssertRCReturn(rc, rc);
2612 pLinPtr->offFirstPage = u32;
2613
2614 /* How many pages. */
2615 rc = SSMR3GetU32(pSSM, &u32);
2616 AssertRCReturn(rc, rc);
2617 pLinPtr->cPages = u32;
2618
2619 uint32_t iPage;
2620 for (iPage = 0; iPage < pLinPtr->cPages; iPage++)
2621 {
2622 /* Array of the GC physical addresses for these pages.
2623 * It is assumed that the physical address of the locked resident
2624 * guest page does not change.
2625 */
2626 RTGCPHYS GCPhysPage;
2627 rc = SSMR3GetGCPhys(pSSM, &GCPhysPage);
2628 AssertRCReturn(rc, rc);
2629
2630 /* Verify that the number of loaded pages is valid. */
2631 cPages++;
2632 if (cPages > pCmd->cLinPtrPages)
2633 {
2634 LogRel(("VMMDevHGCM load state failure: cPages %d, expected %d, ptr %d/%d\n",
2635 cPages, pCmd->cLinPtrPages, i, pCmd->cLinPtrs));
2636 return VERR_SSM_UNEXPECTED_DATA;
2637 }
2638
2639 *pPages++ = GCPhysPage;
2640 }
2641 }
2642 }
2643
2644 /* A reserved field, will allow to extend saved data for a command. */
2645 rc = SSMR3GetU32(pSSM, &u32);
2646 AssertRCReturn(rc, rc);
2647
2648 vmmdevHGCMAddCommand (pThis, pCmd, GCPhys, cbSize, VBOXHGCMCMDTYPE_LOADSTATE);
2649 }
2650
2651 /* A reserved field, will allow to extend saved data for VMMDevHGCM. */
2652 rc = SSMR3GetU32(pSSM, &u32);
2653 AssertRCReturn(rc, rc);
2654 }
2655
2656 return rc;
2657}
2658
2659/** @thread EMT */
2660int vmmdevHGCMLoadStateDone(PVMMDEV pThis, PSSMHANDLE pSSM)
2661{
2662 LogFlowFunc(("\n"));
2663
2664 /* Reissue pending requests. */
2665 PPDMDEVINS pDevIns = pThis->pDevIns;
2666
2667 int rc = vmmdevHGCMCmdListLock (pThis);
2668
2669 if (RT_SUCCESS (rc))
2670 {
2671 /* Start from the current list head and commands loaded from saved state.
2672 * New commands will be inserted at the list head, so they will not be seen by
2673 * this loop.
2674 *
2675 * Note: The list contains only VBOXHGCMCMD structures, place for HGCM parameters
2676 * and for data buffers has not been allocated.
2677 * Command handlers compute the command size and reallocate it before
2678 * resubmitting the command to HGCM services.
2679 * New commands will be inserted to the list.
2680 */
2681 PVBOXHGCMCMD pIter = pThis->pHGCMCmdList;
2682
2683 pThis->pHGCMCmdList = NULL; /* Reset the list. Saved commands will be processed and deallocated. */
2684
2685 while (pIter)
2686 {
2687 /* This will remove the command from the list if resubmitting fails. */
2688 bool fHGCMCalled = false;
2689
2690 LogFlowFunc (("pIter %p\n", pIter));
2691
2692 PVBOXHGCMCMD pNext = pIter->pNext;
2693
2694 PVBOXHGCMCMD pCmd = NULL; /* Resubmitted command. */
2695
2696 VMMDevHGCMRequestHeader *requestHeader = (VMMDevHGCMRequestHeader *)RTMemAllocZ (pIter->cbSize);
2697 Assert(requestHeader);
2698 if (requestHeader == NULL)
2699 return VERR_NO_MEMORY;
2700
2701 PDMDevHlpPhysRead(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize);
2702
2703 /* the structure size must be greater or equal to the header size */
2704 if (requestHeader->header.size < sizeof(VMMDevHGCMRequestHeader))
2705 {
2706 Log(("VMMDev request header size too small! size = %d\n", requestHeader->header.size));
2707 }
2708 else
2709 {
2710 /* check the version of the header structure */
2711 if (requestHeader->header.version != VMMDEV_REQUEST_HEADER_VERSION)
2712 {
2713 Log(("VMMDev: guest header version (0x%08X) differs from ours (0x%08X)\n", requestHeader->header.version, VMMDEV_REQUEST_HEADER_VERSION));
2714 }
2715 else
2716 {
2717 Log(("VMMDev request issued: %d, command type %d\n", requestHeader->header.requestType, pIter->enmCmdType));
2718
2719 /* Use the saved command type. Even if the guest has changed the memory already,
2720 * HGCM should see the same command as it was before saving state.
2721 */
2722 switch (pIter->enmCmdType)
2723 {
2724 case VBOXHGCMCMDTYPE_CONNECT:
2725 {
2726 if (requestHeader->header.size < sizeof(VMMDevHGCMConnect))
2727 {
2728 AssertMsgFailed(("VMMDevReq_HGCMConnect structure has invalid size!\n"));
2729 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2730 }
2731 else if (!pThis->pHGCMDrv)
2732 {
2733 Log(("VMMDevReq_HGCMConnect HGCM Connector is NULL!\n"));
2734 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2735 }
2736 else
2737 {
2738 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)requestHeader;
2739
2740 Log(("VMMDevReq_HGCMConnect\n"));
2741
2742 requestHeader->header.rc = vmmdevHGCMConnectSaved (pThis, pHGCMConnect, pIter->GCPhys, &fHGCMCalled, pIter, &pCmd);
2743 }
2744 break;
2745 }
2746
2747 case VBOXHGCMCMDTYPE_DISCONNECT:
2748 {
2749 if (requestHeader->header.size < sizeof(VMMDevHGCMDisconnect))
2750 {
2751 AssertMsgFailed(("VMMDevReq_HGCMDisconnect structure has invalid size!\n"));
2752 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2753 }
2754 else if (!pThis->pHGCMDrv)
2755 {
2756 Log(("VMMDevReq_HGCMDisconnect HGCM Connector is NULL!\n"));
2757 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2758 }
2759 else
2760 {
2761 VMMDevHGCMDisconnect *pHGCMDisconnect = (VMMDevHGCMDisconnect *)requestHeader;
2762
2763 Log(("VMMDevReq_VMMDevHGCMDisconnect\n"));
2764 requestHeader->header.rc = vmmdevHGCMDisconnectSaved (pThis, pHGCMDisconnect, pIter->GCPhys, &fHGCMCalled, pIter, &pCmd);
2765 }
2766 break;
2767 }
2768
2769 case VBOXHGCMCMDTYPE_CALL:
2770 {
2771 if (requestHeader->header.size < sizeof(VMMDevHGCMCall))
2772 {
2773 AssertMsgFailed(("VMMDevReq_HGCMCall structure has invalid size!\n"));
2774 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2775 }
2776 else if (!pThis->pHGCMDrv)
2777 {
2778 Log(("VMMDevReq_HGCMCall HGCM Connector is NULL!\n"));
2779 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2780 }
2781 else
2782 {
2783 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)requestHeader;
2784
2785 Log(("VMMDevReq_HGCMCall: sizeof (VMMDevHGCMRequest) = %04X\n", sizeof (VMMDevHGCMCall)));
2786
2787 Log(("%.*Rhxd\n", requestHeader->header.size, requestHeader));
2788
2789#ifdef VBOX_WITH_64_BITS_GUESTS
2790 bool f64Bits = (requestHeader->header.requestType == VMMDevReq_HGCMCall64);
2791#else
2792 bool f64Bits = false;
2793#endif /* VBOX_WITH_64_BITS_GUESTS */
2794 requestHeader->header.rc = vmmdevHGCMCallSaved (pThis, pHGCMCall, pIter->GCPhys, requestHeader->header.size, f64Bits, &fHGCMCalled, pIter, &pCmd);
2795 }
2796 break;
2797 }
2798 case VBOXHGCMCMDTYPE_LOADSTATE:
2799 {
2800 /* Old saved state. */
2801 switch (requestHeader->header.requestType)
2802 {
2803 case VMMDevReq_HGCMConnect:
2804 {
2805 if (requestHeader->header.size < sizeof(VMMDevHGCMConnect))
2806 {
2807 AssertMsgFailed(("VMMDevReq_HGCMConnect structure has invalid size!\n"));
2808 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2809 }
2810 else if (!pThis->pHGCMDrv)
2811 {
2812 Log(("VMMDevReq_HGCMConnect HGCM Connector is NULL!\n"));
2813 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2814 }
2815 else
2816 {
2817 VMMDevHGCMConnect *pHGCMConnect = (VMMDevHGCMConnect *)requestHeader;
2818
2819 Log(("VMMDevReq_HGCMConnect\n"));
2820
2821 requestHeader->header.rc = vmmdevHGCMConnect (pThis, pHGCMConnect, pIter->GCPhys);
2822 }
2823 break;
2824 }
2825
2826 case VMMDevReq_HGCMDisconnect:
2827 {
2828 if (requestHeader->header.size < sizeof(VMMDevHGCMDisconnect))
2829 {
2830 AssertMsgFailed(("VMMDevReq_HGCMDisconnect structure has invalid size!\n"));
2831 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2832 }
2833 else if (!pThis->pHGCMDrv)
2834 {
2835 Log(("VMMDevReq_HGCMDisconnect HGCM Connector is NULL!\n"));
2836 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2837 }
2838 else
2839 {
2840 VMMDevHGCMDisconnect *pHGCMDisconnect = (VMMDevHGCMDisconnect *)requestHeader;
2841
2842 Log(("VMMDevReq_VMMDevHGCMDisconnect\n"));
2843 requestHeader->header.rc = vmmdevHGCMDisconnect (pThis, pHGCMDisconnect, pIter->GCPhys);
2844 }
2845 break;
2846 }
2847
2848#ifdef VBOX_WITH_64_BITS_GUESTS
2849 case VMMDevReq_HGCMCall64:
2850 case VMMDevReq_HGCMCall32:
2851#else
2852 case VMMDevReq_HGCMCall:
2853#endif /* VBOX_WITH_64_BITS_GUESTS */
2854 {
2855 if (requestHeader->header.size < sizeof(VMMDevHGCMCall))
2856 {
2857 AssertMsgFailed(("VMMDevReq_HGCMCall structure has invalid size!\n"));
2858 requestHeader->header.rc = VERR_INVALID_PARAMETER;
2859 }
2860 else if (!pThis->pHGCMDrv)
2861 {
2862 Log(("VMMDevReq_HGCMCall HGCM Connector is NULL!\n"));
2863 requestHeader->header.rc = VERR_NOT_SUPPORTED;
2864 }
2865 else
2866 {
2867 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)requestHeader;
2868
2869 Log(("VMMDevReq_HGCMCall: sizeof (VMMDevHGCMRequest) = %04X\n", sizeof (VMMDevHGCMCall)));
2870
2871 Log(("%.*Rhxd\n", requestHeader->header.size, requestHeader));
2872
2873#ifdef VBOX_WITH_64_BITS_GUESTS
2874 bool f64Bits = (requestHeader->header.requestType == VMMDevReq_HGCMCall64);
2875#else
2876 bool f64Bits = false;
2877#endif /* VBOX_WITH_64_BITS_GUESTS */
2878 requestHeader->header.rc = vmmdevHGCMCall (pThis, pHGCMCall, requestHeader->header.size, pIter->GCPhys, f64Bits);
2879 }
2880 break;
2881 }
2882 default:
2883 AssertMsgFailed(("Unknown request type %x during LoadState\n", requestHeader->header.requestType));
2884 LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->header.requestType));
2885 }
2886 } break;
2887
2888 default:
2889 AssertMsgFailed(("Unknown request type %x during LoadState\n", requestHeader->header.requestType));
2890 LogRel(("VMMDEV: Ignoring unknown request type %x during LoadState\n", requestHeader->header.requestType));
2891 }
2892 }
2893 }
2894
2895 if (pIter->enmCmdType == VBOXHGCMCMDTYPE_LOADSTATE)
2896 {
2897 /* Old saved state. */
2898
2899 /* Write back the request */
2900 PDMDevHlpPhysWrite(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize);
2901 RTMemFree(requestHeader);
2902 requestHeader = NULL;
2903 }
2904 else
2905 {
2906 if (!fHGCMCalled)
2907 {
2908 /* HGCM was not called. Return the error to the guest. Guest may try to repeat the call. */
2909 requestHeader->header.rc = VERR_TRY_AGAIN;
2910 requestHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
2911 }
2912
2913 /* Write back the request */
2914 PDMDevHlpPhysWrite(pDevIns, pIter->GCPhys, requestHeader, pIter->cbSize);
2915 RTMemFree(requestHeader);
2916 requestHeader = NULL;
2917
2918 if (!fHGCMCalled)
2919 {
2920 /* HGCM was not called. Deallocate the current command and then notify guest. */
2921 if (pCmd)
2922 {
2923 vmmdevHGCMRemoveCommand (pThis, pCmd);
2924
2925 if (pCmd->paLinPtrs != NULL)
2926 {
2927 RTMemFree(pCmd->paLinPtrs);
2928 }
2929
2930 RTMemFree(pCmd);
2931 pCmd = NULL;
2932 }
2933
2934 VMMDevNotifyGuest (pThis, VMMDEV_EVENT_HGCM);
2935 }
2936 }
2937
2938 /* Deallocate the saved command structure. */
2939 if (pIter->paLinPtrs != NULL)
2940 {
2941 RTMemFree(pIter->paLinPtrs);
2942 }
2943
2944 RTMemFree(pIter);
2945
2946 pIter = pNext;
2947 }
2948
2949 vmmdevHGCMCmdListUnlock (pThis);
2950 }
2951
2952 return rc;
2953}
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