VirtualBox

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

Last change on this file since 1562 was 1562, checked in by vboxsync, 18 years ago

Forgot to include offset into first page into page number calculation in another place as well.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.3 KB
Line 
1/** @file
2 *
3 * VBox Guest/VMM/host communication:
4 * HGCM - Host-Guest Communication Manager device
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24#include <iprt/alloc.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#define LOG_GROUP LOG_GROUP_DEV_VMM
33#include <VBox/log.h>
34
35#include "VMMDevHGCM.h"
36
37/* Information about a linear ptr parameter. */
38typedef struct _VBOXHGCMLINPTR
39{
40 /* Index of the parameter. */
41 int iParm;
42
43 /* Offset in the first physical page of the region. */
44 size_t cbOffsetFirstPage;
45
46 /* How many pages. */
47 uint32_t cPages;
48
49 /* Pointer to array of the HC addresses for these pages.
50 * It is assumed that the HC address of the locked resident
51 * guest physical page does not change.
52 */
53 RTHCPTR *paPages;
54
55} VBOXHGCMLINPTR;
56
57struct VBOXHGCMCMD
58{
59 /* Pointer to guest request. */
60 VMMDevHGCMRequestHeader *pHeader;
61
62 /* Pointer to converted host parameters in case of a Call request. */
63 VBOXHGCMSVCPARM *paHostParms;
64
65 /* Linear pointer parameters information. */
66 int cLinPtrs;
67
68 /* Pointer to descriptions of linear pointers. */
69 VBOXHGCMLINPTR *paLinPtrs;
70};
71
72static int vmmdevHGCMSaveLinPtr (PPDMDEVINS pDevIns,
73 uint32_t iParm,
74 RTGCPTR GCPtr,
75 uint32_t u32Size,
76 uint32_t iLinPtr,
77 VBOXHGCMLINPTR *paLinPtrs,
78 RTHCPTR **ppPages)
79{
80 int rc = VINF_SUCCESS;
81
82 AssertRelease (u32Size > 0);
83
84 VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr];
85
86 /* Take the offset into the current page also into account! */
87 u32Size += GCPtr & PAGE_OFFSET_MASK;
88
89 uint32_t cPages = (u32Size + PAGE_SIZE - 1) / PAGE_SIZE;
90
91 Log(("vmmdevHGCMSaveLinPtr: parm %d: %VGv %d = %d pages\n", iParm, GCPtr, u32Size, cPages));
92
93 pLinPtr->iParm = iParm;
94 pLinPtr->cbOffsetFirstPage = (RTGCUINTPTR)GCPtr & PAGE_OFFSET_MASK;
95 pLinPtr->cPages = cPages;
96 pLinPtr->paPages = *ppPages;
97
98 *ppPages += cPages;
99
100 uint32_t iPage = 0;
101
102 GCPtr &= PAGE_BASE_GC_MASK;
103
104 /* Gonvert the guest linear pointers of pages to HC addresses. */
105 while (iPage < cPages)
106 {
107 /* convert */
108 RTHCPTR HCPtr;
109
110 rc = pDevIns->pDevHlp->pfnPhysGCPtr2HCPtr (pDevIns, GCPtr, &HCPtr);
111// rc = PGMPhysGCPtr2HCPtr (pVM, GCPtr, &HCPtr);
112
113 Log(("vmmdevHGCMSaveLinPtr: Page %d: %VGv -> %p. %Vrc\n", iPage, GCPtr, HCPtr, rc));
114
115 if (VBOX_FAILURE (rc))
116 {
117 break;
118 }
119
120 /* store */
121 pLinPtr->paPages[iPage++] = HCPtr;
122
123 /* next */
124 GCPtr += PAGE_SIZE;
125 }
126
127 AssertRelease (iPage == cPages);
128
129 return rc;
130}
131
132static int vmmdevHGCMWriteLinPtr (PPDMDEVINS pDevIns,
133 uint32_t iParm,
134 void *pvHost,
135 uint32_t u32Size,
136 uint32_t iLinPtr,
137 VBOXHGCMLINPTR *paLinPtrs)
138{
139 int rc = VINF_SUCCESS;
140
141 VBOXHGCMLINPTR *pLinPtr = &paLinPtrs[iLinPtr];
142
143 AssertRelease (u32Size > 0 && iParm == (uint32_t)pLinPtr->iParm);
144
145 uint8_t *pu8Dst = (uint8_t *)pLinPtr->paPages[0] + pLinPtr->cbOffsetFirstPage;
146 uint8_t *pu8Src = (uint8_t *)pvHost;
147
148 Log(("vmmdevHGCMWriteLinPtr: parm %d: size %d, cPages = %d\n", iParm, u32Size, pLinPtr->cPages));
149
150 uint32_t iPage = 0;
151
152 while (iPage < pLinPtr->cPages)
153 {
154 /* copy */
155 size_t cbWrite = iPage == 0?
156 PAGE_SIZE - pLinPtr->cbOffsetFirstPage:
157 PAGE_SIZE;
158
159 Log(("vmmdevHGCMWriteLinPtr: page %d: dst %p, src %p, cbWrite %d\n", iPage, pu8Dst, pu8Src, cbWrite));
160
161 iPage++;
162
163 if (cbWrite >= u32Size)
164 {
165 memcpy (pu8Dst, pu8Src, u32Size);
166 u32Size = 0;
167 break;
168 }
169
170 memcpy (pu8Dst, pu8Src, cbWrite);
171
172 /* next */
173 u32Size -= cbWrite;
174 pu8Src += cbWrite;
175
176 pu8Dst = (uint8_t *)pLinPtr->paPages[iPage];
177 }
178
179 AssertRelease (iPage == pLinPtr->cPages);
180 Assert(u32Size == 0);
181
182 return rc;
183}
184
185int vmmdevHGCMConnect (VMMDevState *pVMMDevState, VMMDevHGCMConnect *pHGCMConnect)
186{
187 int rc = VINF_SUCCESS;
188
189 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAlloc (sizeof (struct VBOXHGCMCMD));
190
191 if (pCmd)
192 {
193 pCmd->pHeader = &pHGCMConnect->header;
194 pCmd->paHostParms = NULL;
195 pCmd->cLinPtrs = 0;
196 pCmd->paLinPtrs = NULL;
197
198 /* Only allow the guest to use existing services! */
199 Assert(pHGCMConnect->loc.type == VMMDevHGCMLoc_LocalHost_Existing);
200 pHGCMConnect->loc.type = VMMDevHGCMLoc_LocalHost_Existing;
201
202 rc = pVMMDevState->pHGCMDrv->pfnConnect (pVMMDevState->pHGCMDrv, pCmd, &pHGCMConnect->loc, &pHGCMConnect->u32ClientID);
203 }
204 else
205 {
206 rc = VERR_NO_MEMORY;
207 }
208
209 return rc;
210}
211
212int vmmdevHGCMDisconnect (VMMDevState *pVMMDevState, VMMDevHGCMDisconnect *pHGCMDisconnect)
213{
214 int rc = VINF_SUCCESS;
215
216 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAlloc (sizeof (struct VBOXHGCMCMD));
217
218 if (pCmd)
219 {
220 pCmd->pHeader = &pHGCMDisconnect->header;
221 pCmd->paHostParms = NULL;
222 pCmd->cLinPtrs = 0;
223 pCmd->paLinPtrs = NULL;
224
225 rc = pVMMDevState->pHGCMDrv->pfnDisconnect (pVMMDevState->pHGCMDrv, pCmd, pHGCMDisconnect->u32ClientID);
226 }
227 else
228 {
229 rc = VERR_NO_MEMORY;
230 }
231
232 return rc;
233}
234
235
236int vmmdevHGCMCall (VMMDevState *pVMMDevState, VMMDevHGCMCall *pHGCMCall)
237{
238 int rc = VINF_SUCCESS;
239
240 Log(("vmmdevHGCMCall: client id = %d, function = %d\n", pHGCMCall->u32ClientID, pHGCMCall->u32Function));
241
242 /* Compute size and allocate memory block to hold:
243 * struct VBOXHGCMCMD
244 * VBOXHGCMSVCPARM[cParms]
245 * memory buffers for pointer parameters.
246 */
247
248 uint32_t cParms = pHGCMCall->cParms;
249
250 Log(("vmmdevHGCMCall: cParms = %d\n", cParms));
251
252 /*
253 * Compute size of required memory buffer.
254 */
255
256 uint32_t cbCmdSize = sizeof (struct VBOXHGCMCMD) + cParms * sizeof (VBOXHGCMSVCPARM);
257
258 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
259
260 /* Look for pointer parameters, which require a host buffer. */
261 uint32_t i;
262
263 uint32_t cLinPtrs = 0;
264 uint32_t cLinPtrPages = 0;
265
266 for (i = 0; i < cParms && VBOX_SUCCESS(rc); i++, pGuestParm++)
267 {
268 switch (pGuestParm->type)
269 {
270 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
271 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
272 case VMMDevHGCMParmType_LinAddr: /* In & Out */
273#if 0
274 case VMMDevHGCMParmType_Virt16Addr:
275 case VMMDevHGCMParmType_VirtAddr:
276#endif
277 {
278 cbCmdSize += pGuestParm->u.Pointer.size;
279
280 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
281 {
282 cLinPtrs++;
283 /* Take the offset into the current page also into account! */
284 cLinPtrPages += ((pGuestParm->u.Pointer.u.linearAddr & PAGE_OFFSET_MASK)
285 + pGuestParm->u.Pointer.size + PAGE_SIZE - 1) / PAGE_SIZE;
286 }
287
288 Log(("vmmdevHGCMCall: linptr size = %d\n", pGuestParm->u.Pointer.size));
289 } break;
290 case VMMDevHGCMParmType_32bit:
291 case VMMDevHGCMParmType_64bit:
292 case VMMDevHGCMParmType_PhysAddr:
293 {
294 } break;
295 default:
296 {
297 rc = VERR_INVALID_PARAMETER;
298 break;
299 }
300 }
301 }
302
303 if (VBOX_FAILURE (rc))
304 {
305 return rc;
306 }
307
308 PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAlloc (cbCmdSize);
309
310 if (pCmd == NULL)
311 {
312 return VERR_NO_MEMORY;
313 }
314
315 pCmd->pHeader = &pHGCMCall->header;
316 pCmd->paHostParms = NULL;
317 pCmd->cLinPtrs = cLinPtrs;
318
319 if (cLinPtrs > 0)
320 {
321 pCmd->paLinPtrs = (VBOXHGCMLINPTR *)RTMemAlloc ( sizeof (VBOXHGCMLINPTR) * cLinPtrs
322 + sizeof (RTHCPTR) * cLinPtrPages);
323
324 if (pCmd->paLinPtrs == NULL)
325 {
326 RTMemFree (pCmd);
327 return VERR_NO_MEMORY;
328 }
329 }
330 else
331 {
332 pCmd->paLinPtrs = NULL;
333 }
334
335 /* Process parameters, changing them to host context pointers for easy
336 * processing by connector. Guest must insure that the pointed data is actually
337 * in the guest RAM and remains locked there for entire request processing.
338 */
339
340 if (cParms != 0)
341 {
342 /* Compute addresses of host parms array and first memory buffer. */
343 VBOXHGCMSVCPARM *pHostParm = (VBOXHGCMSVCPARM *)((char *)pCmd + sizeof (struct VBOXHGCMCMD));
344
345 uint8_t *pcBuf = (uint8_t *)pHostParm + cParms * sizeof (VBOXHGCMSVCPARM);
346
347 pCmd->paHostParms = pHostParm;
348
349 pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
350
351 uint32_t iLinPtr = 0;
352 RTHCPTR *pPages = (RTHCPTR *)((uint8_t *)pCmd->paLinPtrs + sizeof (VBOXHGCMLINPTR) *cLinPtrs);
353
354 for (i = 0; i < cParms && VBOX_SUCCESS(rc); i++, pGuestParm++, pHostParm++)
355 {
356 switch (pGuestParm->type)
357 {
358 case VMMDevHGCMParmType_32bit:
359 {
360 uint32_t u32 = pGuestParm->u.value32;
361
362 pHostParm->type = VBOX_HGCM_SVC_PARM_32BIT;
363 pHostParm->u.uint32 = u32;
364
365 Log(("vmmdevHGCMCall: uint32 guest parameter %u\n", u32));
366 } break;
367
368 case VMMDevHGCMParmType_64bit:
369 {
370 uint64_t u64 = pGuestParm->u.value64;
371
372 pHostParm->type = VBOX_HGCM_SVC_PARM_64BIT;
373 pHostParm->u.uint64 = u64;
374
375 Log(("vmmdevHGCMCall: uint64 guest parameter %llu\n", u64));
376 } break;
377
378 case VMMDevHGCMParmType_PhysAddr:
379 {
380 uint32_t size = pGuestParm->u.Pointer.size;
381 RTGCPHYS physAddr = pGuestParm->u.Pointer.u.physAddr;
382
383 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
384 pHostParm->u.pointer.size = size;
385
386 rc = pVMMDevState->pDevIns->pDevHlp->pfnPhys2HCVirt (pVMMDevState->pDevIns, physAddr,
387 size, &pHostParm->u.pointer.addr);
388
389 Log(("vmmdevHGCMCall: PhysAddr guest parameter %VGp\n", physAddr));
390 } break;
391
392 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
393 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
394 case VMMDevHGCMParmType_LinAddr: /* In & Out */
395 {
396 uint32_t size = pGuestParm->u.Pointer.size;
397 RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr;
398
399 pHostParm->type = VBOX_HGCM_SVC_PARM_PTR;
400 pHostParm->u.pointer.size = size;
401
402 /* Copy guest data to an allocated buffer, so
403 * services can use the data.
404 */
405
406 if (size == 0)
407 {
408 pHostParm->u.pointer.addr = (void *) 0xfeeddead;
409 }
410 else
411 {
412 /* Don't overdo it */
413 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_Out)
414 {
415 rc = pVMMDevState->pDevIns->pDevHlp->pfnPhysReadGCVirt (pVMMDevState->pDevIns, pcBuf,
416 linearAddr, size);
417 }
418 else
419 rc = VINF_SUCCESS;
420
421 if (VBOX_SUCCESS(rc))
422 {
423 pHostParm->u.pointer.addr = pcBuf;
424 pcBuf += size;
425
426 if (pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
427 {
428 /* Remember the guest physical pages that belong to the virtual address
429 * region.
430 */
431 rc = vmmdevHGCMSaveLinPtr (pVMMDevState->pDevIns, i, linearAddr, size, iLinPtr++, pCmd->paLinPtrs, &pPages);
432 }
433 }
434 }
435
436 Log(("vmmdevHGCMCall: LinAddr guest parameter %VGv, rc = %Vrc\n", linearAddr, rc));
437 } break;
438
439 /* just to shut up gcc */
440 default:
441 break;
442 }
443 }
444 }
445
446 if (VBOX_SUCCESS (rc))
447 {
448 /* Pass the function call to HGCM connector for actual processing */
449 rc = pVMMDevState->pHGCMDrv->pfnCall (pVMMDevState->pHGCMDrv, pCmd, pHGCMCall->u32ClientID, pHGCMCall->u32Function, cParms, pCmd->paHostParms);
450 }
451 else
452 {
453 if (pCmd->paLinPtrs)
454 {
455 RTMemFree (pCmd->paLinPtrs);
456 }
457
458 RTMemFree (pCmd);
459 }
460
461 return rc;
462}
463
464#define PDMIHGCMPORT_2_VMMDEVSTATE(pInterface) ( (VMMDevState *) ((uintptr_t)pInterface - RT_OFFSETOF(VMMDevState, HGCMPort)) )
465
466DECLCALLBACK(void) hgcmCompleted (PPDMIHGCMPORT pInterface, int32_t result, PVBOXHGCMCMD pCmd)
467{
468 int rc = VINF_SUCCESS;
469
470 VMMDevHGCMRequestHeader *pHeader = pCmd->pHeader;
471
472 VMMDevState *pVMMDevState = PDMIHGCMPORT_2_VMMDEVSTATE(pInterface);
473
474 /* Setup return codes. */
475 pHeader->result = result;
476
477 /* Update parameters and data buffers. */
478
479 if (pHeader->header.requestType == VMMDevReq_HGCMCall)
480 {
481 VMMDevHGCMCall *pHGCMCall = (VMMDevHGCMCall *)pHeader;
482
483 uint32_t cParms = pHGCMCall->cParms;
484
485 HGCMFunctionParameter *pGuestParm = VMMDEV_HGCM_CALL_PARMS(pHGCMCall);
486
487 VBOXHGCMSVCPARM *pHostParm = pCmd->paHostParms;
488
489 uint32_t i;
490 uint32_t iLinPtr = 0;
491
492 for (i = 0; i < cParms; i++, pGuestParm++, pHostParm++)
493 {
494 switch (pGuestParm->type)
495 {
496 case VMMDevHGCMParmType_32bit:
497 {
498 pGuestParm->u.value32 = pHostParm->u.uint32;
499 } break;
500
501 case VMMDevHGCMParmType_64bit:
502 {
503 pGuestParm->u.value64 = pHostParm->u.uint64;
504 } break;
505
506 case VMMDevHGCMParmType_PhysAddr:
507 {
508 /* do nothing */
509 } break;
510
511 case VMMDevHGCMParmType_LinAddr_In: /* In (read) */
512 case VMMDevHGCMParmType_LinAddr_Out: /* Out (write) */
513 case VMMDevHGCMParmType_LinAddr: /* In & Out */
514 {
515 /* Copy buffer back to guest memory. */
516 uint32_t size = pGuestParm->u.Pointer.size;
517
518 if (size > 0 && pGuestParm->type != VMMDevHGCMParmType_LinAddr_In)
519 {
520 /* Use the saved page list. */
521 rc = vmmdevHGCMWriteLinPtr (pVMMDevState->pDevIns, i, pHostParm->u.pointer.addr, size, iLinPtr++, pCmd->paLinPtrs);
522
523// RTGCPTR linearAddr = pGuestParm->u.Pointer.u.linearAddr;
524//
525// rc = pVMMDevState->pDevIns->pDevHlp->pfnPhysWriteGCVirt (pVMMDevState->pDevIns,
526// linearAddr,
527// pHostParm->u.pointer.addr,
528// size);
529 AssertReleaseRC(rc);
530 }
531 } break;
532
533 default:
534 {
535 /* This indicates that the guest request memory was corrupted. */
536 AssertReleaseMsgFailed(("hgcmCompleted: invalid parameter type %08X\n", pGuestParm->type));
537 }
538 }
539 }
540 }
541
542 /* Mark request as processed*/
543 pHeader->fu32Flags |= VBOX_HGCM_REQ_DONE;
544
545 VMMDevNotifyGuest (pVMMDevState, VMMDEV_EVENT_HGCM);
546
547 if (pCmd->paLinPtrs)
548 {
549 RTMemFree (pCmd->paLinPtrs);
550 }
551
552 RTMemFree (pCmd);
553
554 return;
555}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette