VirtualBox

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

Last change on this file since 13319 was 12917, checked in by vboxsync, 16 years ago

warnings

  • 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 unsigned 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 (index=%d, type=%d)\n",
111 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type));
112 kfree(hgcmR0);
113 return -EINVAL;
114 }
115 }
116 pu8PointerData = kmalloc (cbPointerData, GFP_KERNEL);
117 /* Reconstruct the pointer parameter data in kernel space */
118 if (pu8PointerData == NULL)
119 {
120 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: out of memory allocating %d bytes for pointer data\n",
121 cbPointerData));
122 kfree(hgcmR0);
123 return -ENOMEM;
124 }
125 /* Copy and translate the parameters from the user space structure to the kernel space
126 structure. */
127 for (i = 0; i < hgcmR3->cParms; ++i)
128 {
129 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].type
130 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type;
131 if ( (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type == VMMDevHGCMParmType_LinAddr)
132 || (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
133 == VMMDevHGCMParmType_LinAddr_In))
134 {
135 /* This pointer type means that we are sending data to the host or both
136 sending and reading data. */
137 void *pvR3LinAddr
138 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.u.linearAddr;
139 if (copy_from_user(&pu8PointerData[offPointerData],
140 pvR3LinAddr,
141 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size))
142 {
143 LogRel(("VBOXGUEST_IOCTL_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 back door logger
178 *
179 * @param hgcmR0 The structure to dump.
180 */
181static void vbox_hgcm_dump_params(VBoxGuestHGCMCallInfo *hgcmR0)
182{
183#ifdef DEBUG_Michael
184 int i;
185
186 for (i = 0; i < hgcmR0->cParms; ++i)
187 {
188 switch(VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].type)
189 {
190 case VMMDevHGCMParmType_32bit:
191 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type 32bit: %u\n",
192 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value32));
193 break;
194 case VMMDevHGCMParmType_64bit:
195 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type 64bit: %lu\n",
196 i, VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64));
197 break;
198 case VMMDevHGCMParmType_LinAddr:
199 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type LinAddr, size %u: %.*s...\n",
200 i,
201 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
202 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
203 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
204 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
205 break;
206 case VMMDevHGCMParmType_LinAddr_In:
207 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type LinAddr_In, size %u: %.*s...\n",
208 i,
209 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
210 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
211 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
212 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
213 break;
214 case VMMDevHGCMParmType_LinAddr_Out:
215 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of type LinAddr_Out, size %u: %.*s...\n",
216 i,
217 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size > 10 ?
218 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size : 10,
219 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr,
220 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size));
221 break;
222 default:
223 Log(("VBOXGUEST_IOCTL_HGCM_CALL: parameter %d is of unknown type!", i));
224 }
225 }
226#endif /* defined DEBUG_Michael */
227}
228
229/**
230 * Copy the return parameters from the IOCTL call from kernel to user space and update the
231 * user space ioctl structure with the new parameter information.
232 *
233 * @returns 0 on success, a Linux error code on failure
234 * @param hgcmR3 The user space structure to be updated and copied back to user space, including
235 * the user space addresses of the buffers for the parameters
236 * @param hgcmR0 The kernel space structure pointing to the data to be returned
237 */
238static int vbox_hgcm_return_r0_struct(VBoxGuestHGCMCallInfo *hgcmR3, void *pUser,
239 VBoxGuestHGCMCallInfo *hgcmR0)
240{
241 unsigned i;
242
243 for (i = 0; i < hgcmR3->cParms; ++i)
244 {
245 if ( (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type == VMMDevHGCMParmType_LinAddr)
246 || (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
247 == VMMDevHGCMParmType_LinAddr_Out))
248 {
249 /* We are sending data to the host or sending and reading. */
250 void *pvR3LinAddr
251 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.u.linearAddr;
252 void *pvR0LinAddr
253 = (void *)VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.u.linearAddr;
254 if (copy_to_user(pvR3LinAddr, pvR0LinAddr,
255 VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size))
256 {
257 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: copy_to_user failed!\n"));
258 return -EFAULT;
259 }
260 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.Pointer.size
261 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.Pointer.size;
262 }
263 else if (VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].type
264 != VMMDevHGCMParmType_LinAddr_Out)
265 {
266 /* If it is not a pointer, then it is a 32bit or 64bit value */
267 VBOXGUEST_HGCM_CALL_PARMS(hgcmR3)[i].u.value64
268 = VBOXGUEST_HGCM_CALL_PARMS(hgcmR0)[i].u.value64;
269 }
270 }
271 hgcmR3->result = hgcmR0->result;
272 if (copy_to_user(pUser, hgcmR3,
273 sizeof(*hgcmR3) + hgcmR3->cParms * sizeof(HGCMFunctionParameter)))
274 {
275 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: copy_to_user failed!\n"));
276 return -EFAULT;
277 }
278 return 0;
279}
280
281/**
282 * This IOCTL wrapper allows the guest to make an HGCM call from user space. The
283 * OS-independant part of the Guest Additions already contain code for making an
284 * HGCM call from the guest, but this code assumes that the call is made from the
285 * kernel's address space. So before calling it, we have to copy all parameters
286 * to the HGCM call from user space to kernel space and reconstruct the structures
287 * passed to the call (which include pointers to other memory) inside the kernel's
288 * address space.
289 *
290 * @returns 0 on success or Linux error code on failure
291 * @param arg User space pointer to the call data structure
292 */
293int vbox_ioctl_hgcm_call(unsigned long arg, VBoxDevice *vboxDev)
294{
295 VBoxGuestHGCMCallInfo callHeader, *hgcmR3, *hgcmR0;
296 uint8_t *pu8PointerData;
297 int rc;
298
299 /* Get the call header from user space to see how many call parameters there are. */
300 if (copy_from_user(&callHeader, (void*)arg, sizeof(callHeader)))
301 {
302 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: copy_from_user failed!\n"));
303 return -EFAULT;
304 }
305 /* Copy the ioctl structure from user to kernel space. */
306 rc = vbox_hgcm_get_r3_struct(callHeader.cParms, (void *)arg, &hgcmR3);
307 if (rc != 0)
308 {
309 return rc;
310 }
311 /* Copy the ioctl parameters from user to kernel space and repack the kernel space
312 structure to point to the copied parameters. */
313 rc = vbox_hgcm_get_r3_params(hgcmR3, &hgcmR0, &pu8PointerData);
314 if (rc != 0)
315 {
316 kfree(hgcmR3);
317 return rc;
318 }
319 /* Just to make sure that all that was successful, we read through the contents
320 of the structure we just copied and print them to the log. */
321 vbox_hgcm_dump_params(hgcmR0);
322 /* Call the internal VBoxGuest ioctl interface with the ioctl structure we have just copied. */
323 rc = vboxadd_cmc_call(vboxDev, VBOXGUEST_IOCTL_HGCM_CALL(0), hgcmR0);
324 if (VBOX_FAILURE(rc))
325 {
326 LogRel(("VBOXGUEST_IOCTL_HGCM_CALL: internal ioctl call failed, rc=%Rrc\n", rc));
327 rc = -RTErrConvertToErrno(rc);
328 }
329 else
330 {
331 /* Copy the return parameters back to user space and update the user space structure. */
332 rc = vbox_hgcm_return_r0_struct(hgcmR3, (void *)arg, hgcmR0);
333 }
334 kfree(hgcmR3);
335 kfree(hgcmR0);
336 kfree(pu8PointerData);
337 return rc;
338}
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