VirtualBox

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

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

VMMDevHGCM: RTMemAllocZ instead of memset.

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