VirtualBox

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

Last change on this file since 11519 was 10565, checked in by vboxsync, 16 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 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 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#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
31#include <VBox/log.h>
32#include <iprt/assert.h>
33
34
35/**
36 * Get the IOCTL structure from user space
37 *
38 * @returns 0 on success, Linux error code on failure
39 * @param cParms The number of parameters to the HGCM call
40 * @param pUser User space pointer to the data to be copied
41 * @retval hgcmR3Ret Where to store the structure allocated on success
42 */
43static int vbox_hgcm_get_r3_struct(int cParms, void *pUser, VBoxGuestHGCMCallInfo **hgcmR3Ret)
44{
45 VBoxGuestHGCMCallInfo *hgcmR3;
46
47 /* Allocate space for those call parameters. */
48 hgcmR3 = kmalloc(sizeof(*hgcmR3) + cParms * sizeof(HGCMFunctionParameter), GFP_KERNEL);
49 if (!hgcmR3)
50 {
51 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: cannot allocate memory!\n"));
52 return -ENOMEM;
53 }
54 /* Get the call parameters from user space. */
55 if (copy_from_user(hgcmR3, pUser, sizeof(*hgcmR3) + cParms * sizeof(HGCMFunctionParameter)))
56 {
57 LogRel(("VBOXGUEST_IOCTL_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 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: cannot allocate memory!\n"));
90 return -ENOMEM;
91 }
92 /* Set up the structure header */
93 hgcmR0->u32ClientID = hgcmR3->u32ClientID;
94 hgcmR0->u32Function = hgcmR3->u32Function;
95 hgcmR0->cParms = hgcmR3->cParms;
96 /* Calculate the total size of pointer space. Will normally be for a single pointer */
97 for (i = 0; i < hgcmR3->cParms; ++i)
98 {
99 switch (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type)
100 {
101 case VMMDevHGCMParmType_32bit:
102 case VMMDevHGCMParmType_64bit:
103 break;
104 case VMMDevHGCMParmType_LinAddr:
105 case VMMDevHGCMParmType_LinAddr_In:
106 case VMMDevHGCMParmType_LinAddr_Out:
107 cbPointerData += VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
108 break;
109 default:
110 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: unsupported or unknown parameter type\n"));
111 kfree(hgcmR0);
112 return -EINVAL;
113 }
114 }
115 pu8PointerData = kmalloc (cbPointerData, GFP_KERNEL);
116 /* Reconstruct the pointer parameter data in kernel space */
117 if (pu8PointerData == NULL)
118 {
119 LogRel(("VBOXGUEST_IOCTL_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 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: copy_from_user failed!\n"));
143 kfree(hgcmR0);
144 kfree(pu8PointerData);
145 return -EFAULT;
146 }
147 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr
148 = (vmmDevHypPtr)&pu8PointerData[offPointerData];
149 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size
150 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
151 offPointerData += VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
152 }
153 else if (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
154 == VMMDevHGCMParmType_LinAddr_Out)
155 {
156 /* This type of pointer means that we are reading data from the host. */
157 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr
158 = (vmmDevHypPtr)&pu8PointerData[offPointerData];
159 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size
160 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
161 offPointerData += VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size;
162 }
163 else
164 {
165 /* If it is not a pointer, then it is a 32bit or 64bit value */
166 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64
167 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.value64;
168 }
169 }
170 *hgcmR0Ret = hgcmR0;
171 *ppu8PointerDataRet = pu8PointerData;
172 return 0;
173}
174
175/**
176 * Dump the contents of an hgcm call info structure to the back door logger
177 *
178 * @param hgcmR0 The structure to dump.
179 */
180static void vbox_hgcm_dump_params(VBoxGuestHGCMCallInfo *hgcmR0)
181{
182#ifdef DEBUG_Michael
183 int i;
184
185 for (i = 0; i < hgcmR0->cParms; ++i)
186 {
187 switch(VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].type)
188 {
189 case VMMDevHGCMParmType_32bit:
190 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type 32bit: %u\n",
191 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value32));
192 break;
193 case VMMDevHGCMParmType_64bit:
194 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type 64bit: %lu\n",
195 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64));
196 break;
197 case VMMDevHGCMParmType_LinAddr:
198 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type LinAddr, size %u: %.*s...\n",
199 i,
200 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
201 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
202 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
203 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
204 break;
205 case VMMDevHGCMParmType_LinAddr_In:
206 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type LinAddr_In, size %u: %.*s...\n",
207 i,
208 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
209 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
210 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
211 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
212 break;
213 case VMMDevHGCMParmType_LinAddr_Out:
214 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type LinAddr_Out, 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 default:
222 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of unknown type!", i));
223 }
224 }
225#endif /* defined DEBUG_Michael */
226}
227
228/**
229 * Copy the return parameters from the IOCTL call from kernel to user space and update the
230 * user space ioctl structure with the new parameter information.
231 *
232 * @returns 0 on success, a Linux error code on failure
233 * @param hgcmR3 The user space structure to be updated and copied back to user space, including
234 * the user space addresses of the buffers for the parameters
235 * @param hgcmR0 The kernel space structure pointing to the data to be returned
236 */
237static int vbox_hgcm_return_r0_struct(VBoxGuestHGCMCallInfo *hgcmR3, void *pUser,
238 VBoxGuestHGCMCallInfo *hgcmR0)
239{
240 int i;
241
242 for (i = 0; i < hgcmR3->cParms; ++i)
243 {
244 if ( (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type == VMMDevHGCMParmType_LinAddr)
245 || (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
246 == VMMDevHGCMParmType_LinAddr_Out))
247 {
248 /* We are sending data to the host or sending and reading. */
249 void *pvR3LinAddr
250 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.u.linearAddr;
251 void *pvR0LinAddr
252 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr;
253 if (copy_to_user(pvR3LinAddr, pvR0LinAddr,
254 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size))
255 {
256 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: copy_to_user failed!\n"));
257 return -EFAULT;
258 }
259 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size
260 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size;
261 }
262 else if (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
263 != VMMDevHGCMParmType_LinAddr_Out)
264 {
265 /* If it is not a pointer, then it is a 32bit or 64bit value */
266 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.value64
267 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64;
268 }
269 }
270 hgcmR3->result = hgcmR0->result;
271 if (copy_to_user(pUser, hgcmR3,
272 sizeof(*hgcmR3) + hgcmR3->cParms * sizeof(HGCMFunctionParameter)))
273 {
274 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: copy_to_user failed!\n"));
275 return -EFAULT;
276 }
277 return 0;
278}
279
280/**
281 * This IOCTL wrapper allows the guest to make an HGCM call from user space. The
282 * OS-independant part of the Guest Additions already contain code for making an
283 * HGCM call from the guest, but this code assumes that the call is made from the
284 * kernel's address space. So before calling it, we have to copy all parameters
285 * to the HGCM call from user space to kernel space and reconstruct the structures
286 * passed to the call (which include pointers to other memory) inside the kernel's
287 * address space.
288 *
289 * @returns 0 on success or Linux error code on failure
290 * @param arg User space pointer to the call data structure
291 */
292int vbox_ioctl_hgcm_call(unsigned long arg, VBoxDevice *vboxDev)
293{
294 VBoxGuestHGCMCallInfo callHeader, *hgcmR3, *hgcmR0;
295 uint8_t *pu8PointerData;
296 int rc;
297
298 /* Get the call header from user space to see how many call parameters there are. */
299 if (copy_from_user(&callHeader, (void*)arg, sizeof(callHeader)))
300 {
301 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: copy_from_user failed!\n"));
302 return -EFAULT;
303 }
304 /* Copy the ioctl structure from user to kernel space. */
305 rc = vbox_hgcm_get_r3_struct(callHeader.cParms, (void *)arg, &hgcmR3);
306 if (rc != 0)
307 {
308 return rc;
309 }
310 /* Copy the ioctl parameters from user to kernel space and repack the kernel space
311 structure to point to the copied parameters. */
312 rc = vbox_hgcm_get_r3_params(hgcmR3, &hgcmR0, &pu8PointerData);
313 if (rc != 0)
314 {
315 kfree(hgcmR3);
316 return rc;
317 }
318 /* Just to make sure that all that was successful, we read through the contents
319 of the structure we just copied and print them to the log. */
320 vbox_hgcm_dump_params(hgcmR0);
321 /* Call the internal VBoxGuest ioctl interface with the ioctl structure we have just copied. */
322 rc = vboxadd_cmc_call(vboxDev, VBOXGUEST_IOCTL_HGCM_CALL(0), hgcmR0);
323 if (VBOX_FAILURE(rc))
324 {
325 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: internal ioctl call failed, rc=%Rrc\n", rc));
326 rc = -RTErrConvertToErrno(rc);
327 }
328 else
329 {
330 /* Copy the return parameters back to user space and update the user space structure. */
331 rc = vbox_hgcm_return_r0_struct(hgcmR3, (void *)arg, hgcmR0);
332 }
333 kfree(hgcmR3);
334 kfree(hgcmR0);
335 kfree(pu8PointerData);
336 return rc;
337}
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