VirtualBox

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

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

VMMDev: remove failed HGCM commands from the pending command list

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