VirtualBox

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

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

VMMDevHGCM: do not delete pending HGCM requests when VM is being saved.

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