VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/module/hgcmcall.c@ 4328

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

Biggest check-in ever. New source code headers for all (C) innotek files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.0 KB
Line 
1/** @file
2 *
3 * vboxadd -- VirtualBox Guest Additions for Linux
4 * hgcmcall.c -- wrapper for hgcm calls from user space
5 */
6
7/*
8 * Copyright (C) 2006-2007 innotek GmbH
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 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
19#include "the-linux-kernel.h"
20#include "version-generated.h"
21
22/* #define IRQ_DEBUG */
23
24#include "vboxmod.h"
25#include "waitcompat.h"
26#include <VBox/log.h>
27
28
29/**
30 * Get the IOCTL structure from user space
31 *
32 * @returns 0 on success, Linux error code on failure
33 * @param cParms The number of parameters to the HGCM call
34 * @param pUser User space pointer to the data to be copied
35 * @retval hgcmR3Ret Where to store the structure allocated on success
36 */
37static int vbox_hgcm_get_r3_struct(int cParms, void *pUser, VBoxGuestHGCMCallInfo **hgcmR3Ret)
38{
39 VBoxGuestHGCMCallInfo *hgcmR3;
40
41 /* Allocate space for those call parameters. */
42 hgcmR3 = kmalloc(sizeof(*hgcmR3) + cParms * sizeof(HGCMFunctionParameter), GFP_KERNEL);
43 if (!hgcmR3)
44 {
45 elog("IOCTL_VBOXGUEST_HGCM_CALL: cannot allocate memory!\n");
46 Log(("IOCTL_VBOXGUEST_HGCM_CALL: cannot allocate memory!\n"));
47 return -ENOMEM;
48 }
49 /* Get the call parameters from user space. */
50 if (copy_from_user(hgcmR3, pUser, sizeof(*hgcmR3) + cParms * sizeof(HGCMFunctionParameter)))
51 {
52 elog("IOCTL_VBOXGUEST_HGCM_CALL: copy_from_user failed!\n");
53 Log(("IOCTL_VBOXGUEST_HGCM_CALL: copy_from_user failed!\n"));
54 kfree(hgcmR3);
55 return -EFAULT;
56 }
57 *hgcmR3Ret = hgcmR3;
58 return 0;
59}
60
61/**
62 * Read the IOCTL parameters from the calling user space application and repack them into
63 * a structure in kernel space. This repacking is needed because the structure contains pointers
64 * which will no longer be valid after it is copied from user to kernel space.
65 *
66 * @returns 0 on success, or a Linux error code on failure
67 * @param hgcmR3 The user space ioctl structure, copied into kernel space
68 * @retval hgcmR0Ret The kernel space structure set up in this function
69 * @retval ppu8PointerDataRet A buffer storing the parameters copied from user space
70 */
71static int vbox_hgcm_get_r3_params(VBoxGuestHGCMCallInfo *hgcmR3,
72 VBoxGuestHGCMCallInfo **hgcmR0Ret,
73 uint8_t **ppu8PointerDataRet)
74{
75 VBoxGuestHGCMCallInfo *hgcmR0;
76 uint8_t *pu8PointerData;
77 size_t cbPointerData = 0, offPointerData = 0;
78 int i;
79
80 /* Allocate the structure which we will pass to the kernel space HGCM call. */
81 hgcmR0 = kmalloc(sizeof(*hgcmR0) + hgcmR3->cParms * sizeof(HGCMFunctionParameter),
82 GFP_KERNEL);
83 if (!hgcmR0)
84 {
85 elog("IOCTL_VBOXGUEST_HGCM_CALL: cannot allocate memory!\n");
86 Log(("IOCTL_VBOXGUEST_HGCM_CALL: cannot allocate memory!\n"));
87 return -ENOMEM;
88 }
89 /* Set up the structure header */
90 hgcmR0->u32ClientID = hgcmR3->u32ClientID;
91 hgcmR0->u32Function = hgcmR3->u32Function;
92 hgcmR0->cParms = hgcmR3->cParms;
93 /* Calculate the total size of pointer space. Will normally be for a single pointer */
94 for (i = 0; i < hgcmR3->cParms; ++i)
95 {
96 switch (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type)
97 {
98 case VMMDevHGCMParmType_32bit:
99 case VMMDevHGCMParmType_64bit:
100 break;
101 case VMMDevHGCMParmType_LinAddr:
102 case VMMDevHGCMParmType_LinAddr_In:
103 case VMMDevHGCMParmType_LinAddr_Out:
104 cbPointerData += VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
105 break;
106 default:
107 elog("IOCTL_VBOXGUEST_HGCM_CALL: unsupported or unknown parameter type\n");
108 Log(("IOCTL_VBOXGUEST_HGCM_CALL: unsupported or unknown parameter type\n"));
109 kfree(hgcmR0);
110 return -EINVAL;
111 }
112 }
113 pu8PointerData = kmalloc (cbPointerData, GFP_KERNEL);
114 /* Reconstruct the pointer parameter data in kernel space */
115 if (pu8PointerData == NULL)
116 {
117 elog("IOCTL_VBOXGUEST_HGCM_CALL: out of memory allocating %d bytes for pointer data\n",
118 cbPointerData);
119 Log(("IOCTL_VBOXGUEST_HGCM_CALL: out of memory allocating %d bytes for pointer data\n",
120 cbPointerData));
121 kfree(hgcmR0);
122 return -ENOMEM;
123 }
124 /* Copy and translate the parameters from the user space structure to the kernel space
125 structure. */
126 for (i = 0; i < hgcmR3->cParms; ++i)
127 {
128 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].type
129 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type;
130 if ( (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type == VMMDevHGCMParmType_LinAddr)
131 || (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
132 == VMMDevHGCMParmType_LinAddr_In))
133 {
134 /* This pointer type means that we are sending data to the host or both
135 sending and reading data. */
136 void *pvR3LinAddr
137 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.u.linearAddr;
138 if (copy_from_user(&pu8PointerData[offPointerData],
139 pvR3LinAddr,
140 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size))
141 {
142 elog("IOCTL_VBOXGUEST_HGCM_CALL: copy_from_user failed!\n");
143 Log(("IOCTL_VBOXGUEST_HGCM_CALL: copy_from_user failed!\n"));
144 kfree(hgcmR0);
145 kfree(pu8PointerData);
146 return -EFAULT;
147 }
148 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr
149 = (vmmDevHypPtr)&pu8PointerData[offPointerData];
150 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size
151 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
152 offPointerData += VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
153 }
154 else if (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
155 == VMMDevHGCMParmType_LinAddr_Out)
156 {
157 /* This type of pointer means that we are reading data from the host. */
158 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr
159 = (vmmDevHypPtr)&pu8PointerData[offPointerData];
160 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size
161 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
162 offPointerData += VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
163 }
164 else
165 {
166 /* If it is not a pointer, then it is a 32bit or 64bit value */
167 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64
168 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.value64;
169 }
170 }
171 *hgcmR0Ret = hgcmR0;
172 *ppu8PointerDataRet = pu8PointerData;
173 return 0;
174}
175
176/**
177 * Dump the contents of an hgcm call info structure to the system log and to the back door logger
178 * if it is active.
179 *
180 * @param hgcmR0 The structure to dump.
181 */
182static void vbox_hgcm_dump_params(VBoxGuestHGCMCallInfo *hgcmR0)
183{
184#ifdef DEBUG_Michael
185 int i;
186
187 for (i = 0; i < hgcmR0->cParms; ++i)
188 {
189 switch(VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].type)
190 {
191 case VMMDevHGCMParmType_32bit:
192 elog("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type 32bit: %u\n",
193 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value32);
194 Log(("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type 32bit: %u\n",
195 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value32));
196 break;
197 case VMMDevHGCMParmType_64bit:
198 elog("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type 64bit: %lu\n",
199 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64);
200 Log(("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type 64bit: %lu\n",
201 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64));
202 break;
203 case VMMDevHGCMParmType_LinAddr:
204 elog("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type LinAddr, size %u: %.*s...\n",
205 i,
206 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
207 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
208 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
209 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size);
210 Log(("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type LinAddr, size %u: %.*s...\n",
211 i,
212 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
213 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
214 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
215 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
216 break;
217 case VMMDevHGCMParmType_LinAddr_In:
218 elog("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type LinAddr_In, size %u: %.*s...\n",
219 i,
220 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
221 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
222 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
223 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size);
224 Log(("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type LinAddr_In, size %u: %.*s...\n",
225 i,
226 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
227 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
228 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
229 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
230 break;
231 case VMMDevHGCMParmType_LinAddr_Out:
232 elog("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type LinAddr_Out, size %u: %.*s...\n",
233 i,
234 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
235 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
236 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
237 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size);
238 Log(("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of type LinAddr_Out, size %u: %.*s...\n",
239 i,
240 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
241 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
242 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
243 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
244 break;
245 default:
246 elog("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of unknown type!", i);
247 Log(("IOCTL_VBOXGUEST_HGCM_CALL: parameter %d is of unknown type!", i));
248 }
249 }
250#endif /* defined DEBUG_Michael */
251}
252
253/**
254 * Copy the return parameters from the IOCTL call from kernel to user space and update the
255 * user space ioctl structure with the new parameter information.
256 *
257 * @returns 0 on success, a Linux error code on failure
258 * @param hgcmR3 The user space structure to be updated and copied back to user space, including
259 * the user space addresses of the buffers for the parameters
260 * @param hgcmR0 The kernel space structure pointing to the data to be returned
261 */
262static int vbox_hgcm_return_r0_struct(VBoxGuestHGCMCallInfo *hgcmR3, void *pUser,
263 VBoxGuestHGCMCallInfo *hgcmR0)
264{
265 int i;
266
267 for (i = 0; i < hgcmR3->cParms; ++i)
268 {
269 if ( (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type == VMMDevHGCMParmType_LinAddr)
270 || (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
271 == VMMDevHGCMParmType_LinAddr_Out))
272 {
273 /* We are sending data to the host or sending and reading. */
274 void *pvR3LinAddr
275 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.u.linearAddr;
276 void *pvR0LinAddr
277 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr;
278 if (copy_to_user(pvR3LinAddr, pvR0LinAddr,
279 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size))
280 {
281 elog("IOCTL_VBOXGUEST_HGCM_CALL: copy_to_user failed!\n");
282 Log(("IOCTL_VBOXGUEST_HGCM_CALL: copy_to_user failed!\n"));
283 return -EFAULT;
284 }
285 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size
286 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size;
287 }
288 else if (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
289 != VMMDevHGCMParmType_LinAddr_Out)
290 {
291 /* If it is not a pointer, then it is a 32bit or 64bit value */
292 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.value64
293 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64;
294 }
295 }
296 hgcmR3->result = hgcmR0->result;
297 if (copy_to_user(pUser, hgcmR3,
298 sizeof(*hgcmR3) + hgcmR3->cParms * sizeof(HGCMFunctionParameter)))
299 {
300 elog("IOCTL_VBOXGUEST_HGCM_CALL: copy_to_user failed!\n");
301 Log(("IOCTL_VBOXGUEST_HGCM_CALL: copy_to_user failed!\n"));
302 return -EFAULT;
303 }
304 return 0;
305}
306
307/**
308 * This IOCTL wrapper allows the guest to make an HGCM call from user space. The
309 * OS-independant part of the Guest Additions already contain code for making an
310 * HGCM call from the guest, but this code assumes that the call is made from the
311 * kernel's address space. So before calling it, we have to copy all parameters
312 * to the HGCM call from user space to kernel space and reconstruct the structures
313 * passed to the call (which include pointers to other memory) inside the kernel's
314 * address space.
315 *
316 * @returns 0 on success or Linux error code on failure
317 * @param arg User space pointer to the call data structure
318 */
319int vbox_ioctl_hgcm_call(unsigned long arg, VBoxDevice *vboxDev)
320{
321 VBoxGuestHGCMCallInfo callHeader, *hgcmR3, *hgcmR0;
322 uint8_t *pu8PointerData;
323 int rc;
324
325 compiler_assert(_IOC_SIZE(IOCTL_VBOXGUEST_HGCM_CALL) == sizeof(VBoxGuestHGCMCallInfo));
326 /* Get the call header from user space to see how many call parameters there are. */
327 if (copy_from_user(&callHeader, (void*)arg, sizeof(callHeader)))
328 {
329 elog("IOCTL_VBOXGUEST_HGCM_CALL: copy_from_user failed!\n");
330 Log(("IOCTL_VBOXGUEST_HGCM_CALL: copy_from_user failed!\n"));
331 return -EFAULT;
332 }
333 /* Copy the ioctl structure from user to kernel space. */
334 rc = vbox_hgcm_get_r3_struct(callHeader.cParms, (void *)arg, &hgcmR3);
335 if (rc != 0)
336 {
337 return rc;
338 }
339 /* Copy the ioctl parameters from user to kernel space and repack the kernel space
340 structure to point to the copied parameters. */
341 rc = vbox_hgcm_get_r3_params(hgcmR3, &hgcmR0, &pu8PointerData);
342 if (rc != 0)
343 {
344 kfree(hgcmR3);
345 return rc;
346 }
347 /* Just to make sure that all that was successful, we read through the contents
348 of the structure we just copied and print them to the log. */
349 vbox_hgcm_dump_params(hgcmR0);
350 /* Call the internal VBoxGuest ioctl interface with the ioctl structure we have just copied. */
351 rc = vboxadd_cmc_call(vboxDev, IOCTL_VBOXGUEST_HGCM_CALL, hgcmR0);
352 if (VBOX_FAILURE(rc))
353 {
354 elog("IOCTL_VBOXGUEST_HGCM_CALL: internal ioctl call failed, rc=%d\n", rc);
355 Log(("IOCTL_VBOXGUEST_HGCM_CALL: internal ioctl call failed, rc=%d\n", rc));
356 /** @todo We need a function to convert VBox error codes back to Linux. */
357 rc = -EINVAL;
358 }
359 else
360 {
361 /* Copy the return parameters back to user space and update the user space structure. */
362 rc = vbox_hgcm_return_r0_struct(hgcmR3, (void *)arg, hgcmR0);
363 }
364 kfree(hgcmR3);
365 kfree(hgcmR0);
366 kfree(pu8PointerData);
367 return rc;
368}
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