VirtualBox

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

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

Fix logging in src/VBox/Additions/linux/module/hgcmcall.c

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