VirtualBox

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

Last change on this file since 7183 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

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