VirtualBox

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

Last change on this file since 44023 was 40718, checked in by vboxsync, 13 years ago

VMMDev: saved HGCM commands must not be completed.

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