VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuestLib/HGCMInternal.cpp@ 17275

Last change on this file since 17275 was 15705, checked in by vboxsync, 16 years ago

Todos added

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/** @file
2 *
3 * VBoxGuestLib - A support library for VirtualBox guest additions:
4 * Host-Guest Communication Manager internal functions, implemented by VBoxGuest
5 */
6
7/*
8 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
19 * Clara, CA 95054 USA or visit http://www.sun.com if you need
20 * additional information or have any questions.
21 */
22
23/* Entire file is ifdef'ed with VBGL_VBOXGUEST */
24#ifdef VBGL_VBOXGUEST
25
26#include <VBox/VBoxGuestLib.h>
27#include "VBGLInternal.h"
28#include <iprt/string.h>
29#include <iprt/assert.h>
30#include <iprt/alloca.h>
31
32/* These functions can be only used by VBoxGuest. */
33
34DECLVBGL(int) VbglHGCMConnect (VBoxGuestHGCMConnectInfo *pConnectInfo,
35 VBGLHGCMCALLBACK *pAsyncCallback, void *pvAsyncData,
36 uint32_t u32AsyncData)
37{
38 VMMDevHGCMConnect *pHGCMConnect;
39 int rc;
40
41 if (!pConnectInfo || !pAsyncCallback)
42 return VERR_INVALID_PARAMETER;
43
44 pHGCMConnect = NULL;
45
46 /* Allocate request */
47 rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMConnect, sizeof (VMMDevHGCMConnect), VMMDevReq_HGCMConnect);
48
49 if (RT_SUCCESS(rc))
50 {
51 /* Initialize request memory */
52 pHGCMConnect->header.fu32Flags = 0;
53
54 memcpy (&pHGCMConnect->loc, &pConnectInfo->Loc, sizeof (HGCMServiceLocation));
55 pHGCMConnect->u32ClientID = 0;
56
57 /* Issue request */
58 rc = VbglGRPerform (&pHGCMConnect->header.header);
59
60 if (RT_SUCCESS(rc))
61 {
62 /* Check if host decides to process the request asynchronously. */
63 if (rc == VINF_HGCM_ASYNC_EXECUTE)
64 {
65 /* Wait for request completion interrupt notification from host */
66 pAsyncCallback (&pHGCMConnect->header, pvAsyncData, u32AsyncData);
67 }
68
69 pConnectInfo->result = pHGCMConnect->header.result;
70
71 if (RT_SUCCESS (pConnectInfo->result))
72 pConnectInfo->u32ClientID = pHGCMConnect->u32ClientID;
73 }
74
75 VbglGRFree (&pHGCMConnect->header.header);
76 }
77
78 return rc;
79}
80
81
82DECLVBGL(int) VbglHGCMDisconnect (VBoxGuestHGCMDisconnectInfo *pDisconnectInfo,
83 VBGLHGCMCALLBACK *pAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
84{
85 VMMDevHGCMDisconnect *pHGCMDisconnect;
86 int rc;
87
88 if (!pDisconnectInfo || !pAsyncCallback)
89 return VERR_INVALID_PARAMETER;
90
91 pHGCMDisconnect = NULL;
92
93 /* Allocate request */
94 rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMDisconnect, sizeof (VMMDevHGCMDisconnect), VMMDevReq_HGCMDisconnect);
95
96 if (RT_SUCCESS(rc))
97 {
98 /* Initialize request memory */
99 pHGCMDisconnect->header.fu32Flags = 0;
100
101 pHGCMDisconnect->u32ClientID = pDisconnectInfo->u32ClientID;
102
103 /* Issue request */
104 rc = VbglGRPerform (&pHGCMDisconnect->header.header);
105
106 if (RT_SUCCESS(rc))
107 {
108 /* Check if host decides to process the request asynchronously. */
109 if (rc == VINF_HGCM_ASYNC_EXECUTE)
110 {
111 /* Wait for request completion interrupt notification from host */
112 pAsyncCallback (&pHGCMDisconnect->header, pvAsyncData, u32AsyncData);
113 }
114
115 pDisconnectInfo->result = pHGCMDisconnect->header.result;
116 }
117
118 VbglGRFree (&pHGCMDisconnect->header.header);
119 }
120
121 return rc;
122}
123
124
125/** @todo merge with the one below (use a header file). Too lazy now. */
126DECLVBGL(int) VbglHGCMCall (VBoxGuestHGCMCallInfo *pCallInfo,
127 VBGLHGCMCALLBACK *pAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
128{
129 VMMDevHGCMCall *pHGCMCall;
130 uint32_t cbParms;
131 HGCMFunctionParameter *pParm;
132 unsigned iParm;
133 int rc;
134
135 if (!pCallInfo || !pAsyncCallback || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS)
136 {
137 AssertFailed();
138 return VERR_INVALID_PARAMETER;
139 }
140
141 Log (("VbglHGCMCall: pCallInfo->cParms = %d, pHGCMCall->u32Function = %d\n", pCallInfo->cParms, pCallInfo->u32Function));
142
143 pHGCMCall = NULL;
144
145 cbParms = pCallInfo->cParms * sizeof (HGCMFunctionParameter);
146
147 /* Allocate request */
148 rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMCall, sizeof (VMMDevHGCMCall) + cbParms, VMMDevReq_HGCMCall);
149
150 Log (("VbglHGCMCall Allocated gr %p, rc = %Rrc, cbParms = %d\n", pHGCMCall, rc, cbParms));
151
152 if (RT_SUCCESS(rc))
153 {
154 void *apvCtx[VBOX_HGCM_MAX_PARMS];
155 memset (apvCtx, 0, sizeof(void *) * pCallInfo->cParms);
156
157 /* Initialize request memory */
158 pHGCMCall->header.fu32Flags = 0;
159 pHGCMCall->header.result = VINF_SUCCESS;
160
161 pHGCMCall->u32ClientID = pCallInfo->u32ClientID;
162 pHGCMCall->u32Function = pCallInfo->u32Function;
163 pHGCMCall->cParms = pCallInfo->cParms;
164
165 if (cbParms)
166 {
167 /* Lock user buffers. */
168 pParm = VBOXGUEST_HGCM_CALL_PARMS(pCallInfo);
169
170 for (iParm = 0; iParm < pCallInfo->cParms; iParm++, pParm++)
171 {
172 switch (pParm->type)
173 {
174 case VMMDevHGCMParmType_LinAddr_Locked_In:
175 pParm->type = VMMDevHGCMParmType_LinAddr_In;
176 break;
177 case VMMDevHGCMParmType_LinAddr_Locked_Out:
178 pParm->type = VMMDevHGCMParmType_LinAddr_Out;
179 break;
180 case VMMDevHGCMParmType_LinAddr_Locked:
181 pParm->type = VMMDevHGCMParmType_LinAddr;
182 break;
183
184 case VMMDevHGCMParmType_LinAddr_In:
185 case VMMDevHGCMParmType_LinAddr_Out:
186 case VMMDevHGCMParmType_LinAddr:
187 /* PORTME: When porting this to Darwin and other systems where the entire kernel isn't mapped
188 into every process, all linear address will have to be converted to physical SG lists at
189 this point. Care must also be taken on these guests to not mix kernel and user addresses
190 in HGCM calls, or we'll end up locking the wrong memory. If VMMDev/HGCM gets a linear address
191 it will assume that it's in the current memory context (i.e. use CR3 to translate it).
192
193 These kind of problems actually applies to some patched linux kernels too, including older
194 fedora releases. (The patch is the infamous 4G/4G patch, aka 4g4g, by Ingo Molnar.) */
195 rc = vbglLockLinear (&apvCtx[iParm], (void *)pParm->u.Pointer.u.linearAddr, pParm->u.Pointer.size, (pParm->type == VMMDevHGCMParmType_LinAddr_In) ? false : true /* write access */);
196 break;
197 default:
198 /* make gcc happy */
199 break;
200 }
201 if (RT_FAILURE (rc))
202 break;
203 }
204 memcpy (VMMDEV_HGCM_CALL_PARMS(pHGCMCall), VBOXGUEST_HGCM_CALL_PARMS(pCallInfo), cbParms);
205 }
206
207 /* Check that the parameter locking was ok. */
208 if (RT_SUCCESS(rc))
209 {
210 Log (("calling VbglGRPerform\n"));
211
212 /* Issue request */
213 rc = VbglGRPerform (&pHGCMCall->header.header);
214
215 Log (("VbglGRPerform rc = %Rrc (header rc=%d)\n", rc, pHGCMCall->header.result));
216
217 /** If the call failed, but as a result of the request itself, then pretend success
218 * Upper layers will interpret the result code in the packet.
219 */
220 if (RT_FAILURE(rc) && rc == pHGCMCall->header.result)
221 {
222 Assert(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE);
223 rc = VINF_SUCCESS;
224 }
225
226 if (RT_SUCCESS(rc))
227 {
228 /* Check if host decides to process the request asynchronously. */
229 if (rc == VINF_HGCM_ASYNC_EXECUTE)
230 {
231 /* Wait for request completion interrupt notification from host */
232 Log (("Processing HGCM call asynchronously\n"));
233 pAsyncCallback (&pHGCMCall->header, pvAsyncData, u32AsyncData);
234 }
235
236 if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE)
237 {
238 if (cbParms)
239 {
240 memcpy (VBOXGUEST_HGCM_CALL_PARMS(pCallInfo), VMMDEV_HGCM_CALL_PARMS(pHGCMCall), cbParms);
241 }
242 pCallInfo->result = pHGCMCall->header.result;
243 }
244 else
245 {
246 /* The callback returns without completing the request,
247 * that means the wait was interrrupted. That can happen
248 * if the request times out, the system reboots or the
249 * VBoxService ended abnormally.
250 *
251 * Cancel the request, the host will not write to the
252 * memory related to the cancelled request.
253 */
254 Log (("Cancelling HGCM call\n"));
255 pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED;
256
257 pHGCMCall->header.header.requestType = VMMDevReq_HGCMCancel;
258 VbglGRPerform (&pHGCMCall->header.header);
259 }
260 }
261 }
262
263 /* Unlock user buffers. */
264 pParm = VBOXGUEST_HGCM_CALL_PARMS(pCallInfo);
265
266 for (iParm = 0; iParm < pCallInfo->cParms; iParm++, pParm++)
267 {
268 if ( pParm->type == VMMDevHGCMParmType_LinAddr_In
269 || pParm->type == VMMDevHGCMParmType_LinAddr_Out
270 || pParm->type == VMMDevHGCMParmType_LinAddr)
271 {
272 if (apvCtx[iParm] != NULL)
273 {
274 vbglUnlockLinear (apvCtx[iParm], (void *)pParm->u.Pointer.u.linearAddr, pParm->u.Pointer.size);
275 }
276 }
277 else
278 Assert(!apvCtx[iParm]);
279 }
280
281 if ((pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_CANCELLED) == 0)
282 VbglGRFree (&pHGCMCall->header.header);
283 else
284 rc = VERR_INTERRUPTED;
285 }
286
287 return rc;
288}
289# if ARCH_BITS == 64
290/** @todo merge with the one above (use a header file). Too lazy now. */
291DECLVBGL(int) VbglHGCMCall32 (VBoxGuestHGCMCallInfo *pCallInfo,
292 VBGLHGCMCALLBACK *pAsyncCallback, void *pvAsyncData, uint32_t u32AsyncData)
293{
294 VMMDevHGCMCall *pHGCMCall;
295 uint32_t cbParms;
296 HGCMFunctionParameter32 *pParm;
297 unsigned iParm;
298 int rc;
299
300 if (!pCallInfo || !pAsyncCallback || pCallInfo->cParms > VBOX_HGCM_MAX_PARMS)
301 {
302 AssertFailed();
303 return VERR_INVALID_PARAMETER;
304 }
305
306 Log (("VbglHGCMCall: pCallInfo->cParms = %d, pHGCMCall->u32Function = %d\n", pCallInfo->cParms, pCallInfo->u32Function));
307
308 pHGCMCall = NULL;
309
310 cbParms = pCallInfo->cParms * sizeof (HGCMFunctionParameter32);
311
312 /* Allocate request */
313 rc = VbglGRAlloc ((VMMDevRequestHeader **)&pHGCMCall, sizeof (VMMDevHGCMCall) + cbParms, VMMDevReq_HGCMCall32);
314
315 Log (("VbglHGCMCall Allocated gr %p, rc = %Rrc, cbParms = %d\n", pHGCMCall, rc, cbParms));
316
317 if (RT_SUCCESS(rc))
318 {
319 void *apvCtx[VBOX_HGCM_MAX_PARMS];
320 memset (apvCtx, 0, sizeof(void *) * pCallInfo->cParms);
321
322 /* Initialize request memory */
323 pHGCMCall->header.fu32Flags = 0;
324 pHGCMCall->header.result = VINF_SUCCESS;
325
326 pHGCMCall->u32ClientID = pCallInfo->u32ClientID;
327 pHGCMCall->u32Function = pCallInfo->u32Function;
328 pHGCMCall->cParms = pCallInfo->cParms;
329
330 if (cbParms)
331 {
332 /* Lock user buffers. */
333 pParm = VBOXGUEST_HGCM_CALL_PARMS32(pCallInfo);
334
335 for (iParm = 0; iParm < pCallInfo->cParms; iParm++, pParm++)
336 {
337 switch (pParm->type)
338 {
339 case VMMDevHGCMParmType_LinAddr_Locked_In:
340 pParm->type = VMMDevHGCMParmType_LinAddr_In;
341 break;
342 case VMMDevHGCMParmType_LinAddr_Locked_Out:
343 pParm->type = VMMDevHGCMParmType_LinAddr_Out;
344 break;
345 case VMMDevHGCMParmType_LinAddr_Locked:
346 pParm->type = VMMDevHGCMParmType_LinAddr;
347 break;
348
349 case VMMDevHGCMParmType_LinAddr_In:
350 case VMMDevHGCMParmType_LinAddr_Out:
351 case VMMDevHGCMParmType_LinAddr:
352 /* PORTME: When porting this to Darwin and other systems where the entire kernel isn't mapped
353 into every process, all linear address will have to be converted to physical SG lists at
354 this point. Care must also be taken on these guests to not mix kernel and user addresses
355 in HGCM calls, or we'll end up locking the wrong memory. If VMMDev/HGCM gets a linear address
356 it will assume that it's in the current memory context (i.e. use CR3 to translate it).
357
358 These kind of problems actually applies to some patched linux kernels too, including older
359 fedora releases. (The patch is the infamous 4G/4G patch, aka 4g4g, by Ingo Molnar.) */
360 rc = vbglLockLinear (&apvCtx[iParm], (void *)pParm->u.Pointer.u.linearAddr, pParm->u.Pointer.size, (pParm->type == VMMDevHGCMParmType_LinAddr_In) ? false : true /* write access */);
361 break;
362 default:
363 /* make gcc happy */
364 break;
365 }
366 if (RT_FAILURE (rc))
367 break;
368 }
369 memcpy (VMMDEV_HGCM_CALL_PARMS32(pHGCMCall), VBOXGUEST_HGCM_CALL_PARMS32(pCallInfo), cbParms);
370 }
371
372 /* Check that the parameter locking was ok. */
373 if (RT_SUCCESS(rc))
374 {
375 Log (("calling VbglGRPerform\n"));
376
377 /* Issue request */
378 rc = VbglGRPerform (&pHGCMCall->header.header);
379
380 Log (("VbglGRPerform rc = %Rrc (header rc=%d)\n", rc, pHGCMCall->header.result));
381
382 /** If the call failed, but as a result of the request itself, then pretend success
383 * Upper layers will interpret the result code in the packet.
384 */
385 if (RT_FAILURE(rc) && rc == pHGCMCall->header.result)
386 {
387 Assert(pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE);
388 rc = VINF_SUCCESS;
389 }
390
391 if (RT_SUCCESS(rc))
392 {
393 /* Check if host decides to process the request asynchronously. */
394 if (rc == VINF_HGCM_ASYNC_EXECUTE)
395 {
396 /* Wait for request completion interrupt notification from host */
397 Log (("Processing HGCM call asynchronously\n"));
398 pAsyncCallback (&pHGCMCall->header, pvAsyncData, u32AsyncData);
399 }
400
401 if (pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_DONE)
402 {
403 if (cbParms)
404 memcpy (VBOXGUEST_HGCM_CALL_PARMS32(pCallInfo), VMMDEV_HGCM_CALL_PARMS32(pHGCMCall), cbParms);
405
406 pCallInfo->result = pHGCMCall->header.result;
407 }
408 else
409 {
410 /* The callback returns without completing the request,
411 * that means the wait was interrrupted. That can happen
412 * if the request times out, the system reboots or the
413 * VBoxService ended abnormally.
414 *
415 * Cancel the request, the host will not write to the
416 * memory related to the cancelled request.
417 */
418 Log (("Cancelling HGCM call\n"));
419 pHGCMCall->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED;
420
421 pHGCMCall->header.header.requestType = VMMDevReq_HGCMCancel;
422 VbglGRPerform (&pHGCMCall->header.header);
423 }
424 }
425 }
426
427 /* Unlock user buffers. */
428 pParm = VBOXGUEST_HGCM_CALL_PARMS32(pCallInfo);
429
430 for (iParm = 0; iParm < pCallInfo->cParms; iParm++, pParm++)
431 {
432 if ( pParm->type == VMMDevHGCMParmType_LinAddr_In
433 || pParm->type == VMMDevHGCMParmType_LinAddr_Out
434 || pParm->type == VMMDevHGCMParmType_LinAddr)
435 {
436 if (apvCtx[iParm] != NULL)
437 {
438 vbglUnlockLinear (apvCtx[iParm], (void *)pParm->u.Pointer.u.linearAddr, pParm->u.Pointer.size);
439 }
440 }
441 else
442 Assert(!apvCtx[iParm]);
443 }
444
445 if ((pHGCMCall->header.fu32Flags & VBOX_HGCM_REQ_CANCELLED) == 0)
446 VbglGRFree (&pHGCMCall->header.header);
447 else
448 rc = VERR_INTERRUPTED;
449 }
450
451 return rc;
452}
453# endif /* ARCH_BITS == 64 */
454
455#endif /* VBGL_VBOXGUEST */
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