VirtualBox

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

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

VMMDev: s/VMMDEV_MAX_VMMDEV_PARMS/VMMDEV_MAX_HGCM_PARMS/g, return outside logging contraint if, return VERR_INVALID_PARAMETER because that's closer than VERR_NOT_SUPPORTED (as in 'not supported operation').

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