VirtualBox

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

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

InnoTek -> innotek: all the headers and comments.

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