VirtualBox

source: vbox/trunk/src/VBox/VMM/VMReq.cpp@ 24646

Last change on this file since 24646 was 24467, checked in by vboxsync, 15 years ago

Document VMR3Req* caveats.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 45.9 KB
Line 
1/* $Id: VMReq.cpp 24467 2009-11-06 17:01:09Z vboxsync $ */
2/** @file
3 * VM - Virtual Machine
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_VM
27#include <VBox/mm.h>
28#include <VBox/vmm.h>
29#include "VMInternal.h"
30#include <VBox/vm.h>
31#include <VBox/uvm.h>
32
33#include <VBox/err.h>
34#include <VBox/param.h>
35#include <VBox/log.h>
36#include <iprt/assert.h>
37#include <iprt/asm.h>
38#include <iprt/string.h>
39#include <iprt/time.h>
40#include <iprt/semaphore.h>
41#include <iprt/thread.h>
42
43
44/*******************************************************************************
45* Internal Functions *
46*******************************************************************************/
47static int vmR3ReqProcessOneU(PUVM pUVM, PVMREQ pReq);
48
49
50/**
51 * Allocate and queue a call request.
52 *
53 * If it's desired to poll on the completion of the request set cMillies
54 * to 0 and use VMR3ReqWait() to check for completation. In the other case
55 * use RT_INDEFINITE_WAIT.
56 * The returned request packet must be freed using VMR3ReqFree().
57 *
58 * @returns VBox status code.
59 * Will not return VERR_INTERRUPTED.
60 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
61 *
62 * @param pVM The VM handle.
63 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
64 * one of the following special values:
65 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
66 * @param ppReq Where to store the pointer to the request.
67 * This will be NULL or a valid request pointer not matter what happends.
68 * @param cMillies Number of milliseconds to wait for the request to
69 * be completed. Use RT_INDEFINITE_WAIT to only
70 * wait till it's completed.
71 * @param fFlags A combination of the VMREQFLAGS values.
72 * @param pfnFunction Pointer to the function to call.
73 * @param cArgs Number of arguments following in the ellipsis.
74 * @param ... Function arguments.
75 *
76 * @remarks See remarks on VMR3ReqCallVU.
77 */
78VMMR3DECL(int) VMR3ReqCall(PVM pVM, VMCPUID idDstCpu, PVMREQ *ppReq, unsigned cMillies, uint32_t fFlags,
79 PFNRT pfnFunction, unsigned cArgs, ...)
80{
81 va_list va;
82 va_start(va, cArgs);
83 int rc = VMR3ReqCallVU(pVM->pUVM, idDstCpu, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
84 va_end(va);
85 return rc;
86}
87
88
89/**
90 * Convenience wrapper for VMR3ReqCallU.
91 *
92 * This assumes (1) you're calling a function that returns an VBox status code,
93 * (2) that you want it's return code on success, and (3) that you wish to wait
94 * for ever for it to return.
95 *
96 * @returns VBox status code. In the unlikely event that VMR3ReqCallVU fails,
97 * its status code is return. Otherwise, the status of pfnFunction is
98 * returned.
99 *
100 * @param pVM Pointer to the shared VM structure.
101 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
102 * one of the following special values:
103 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
104 * @param pfnFunction Pointer to the function to call.
105 * @param cArgs Number of arguments following in the ellipsis.
106 * @param ... Function arguments.
107 *
108 * @remarks See remarks on VMR3ReqCallVU.
109 */
110VMMR3DECL(int) VMR3ReqCallWait(PVM pVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
111{
112 PVMREQ pReq;
113 va_list va;
114 va_start(va, cArgs);
115 int rc = VMR3ReqCallVU(pVM->pUVM, idDstCpu, &pReq, RT_INDEFINITE_WAIT, VMREQFLAGS_VBOX_STATUS,
116 pfnFunction, cArgs, va);
117 va_end(va);
118 if (RT_SUCCESS(rc))
119 rc = pReq->iStatus;
120 VMR3ReqFree(pReq);
121 return rc;
122}
123
124
125/**
126 * Convenience wrapper for VMR3ReqCallU.
127 *
128 * This assumes (1) you're calling a function that returns an VBox status code,
129 * (2) that you want it's return code on success, and (3) that you wish to wait
130 * for ever for it to return.
131 *
132 * @returns VBox status code. In the unlikely event that VMR3ReqCallVU fails,
133 * its status code is return. Otherwise, the status of pfnFunction is
134 * returned.
135 *
136 * @param pUVM Pointer to the user mode VM structure.
137 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
138 * one of the following special values:
139 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
140 * @param pfnFunction Pointer to the function to call.
141 * @param cArgs Number of arguments following in the ellipsis.
142 * @param ... Function arguments.
143 *
144 * @remarks See remarks on VMR3ReqCallVU.
145 */
146VMMR3DECL(int) VMR3ReqCallWaitU(PUVM pUVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
147{
148 PVMREQ pReq;
149 va_list va;
150 va_start(va, cArgs);
151 int rc = VMR3ReqCallVU(pUVM, idDstCpu, &pReq, RT_INDEFINITE_WAIT, VMREQFLAGS_VBOX_STATUS,
152 pfnFunction, cArgs, va);
153 va_end(va);
154 if (RT_SUCCESS(rc))
155 rc = pReq->iStatus;
156 VMR3ReqFree(pReq);
157 return rc;
158}
159
160
161/**
162 * Convenience wrapper for VMR3ReqCallU.
163 *
164 * This assumes (1) you're calling a function that returns an VBox status code
165 * and that you do not wish to wait for it to complete.
166 *
167 * @returns VBox status code returned by VMR3ReqCallVU.
168 *
169 * @param pVM Pointer to the shared VM structure.
170 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
171 * one of the following special values:
172 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
173 * @param pfnFunction Pointer to the function to call.
174 * @param cArgs Number of arguments following in the ellipsis.
175 * @param ... Function arguments.
176 *
177 * @remarks See remarks on VMR3ReqCallVU.
178 */
179VMMR3DECL(int) VMR3ReqCallNoWait(PVM pVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
180{
181 va_list va;
182 va_start(va, cArgs);
183 int rc = VMR3ReqCallVU(pVM->pUVM, idDstCpu, NULL, 0, VMREQFLAGS_VBOX_STATUS | VMREQFLAGS_NO_WAIT,
184 pfnFunction, cArgs, va);
185 va_end(va);
186 return rc;
187}
188
189
190/**
191 * Convenience wrapper for VMR3ReqCallU.
192 *
193 * This assumes (1) you're calling a function that returns an VBox status code
194 * and that you do not wish to wait for it to complete.
195 *
196 * @returns VBox status code returned by VMR3ReqCallVU.
197 *
198 * @param pUVM Pointer to the user mode VM structure.
199 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
200 * one of the following special values:
201 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
202 * @param pfnFunction Pointer to the function to call.
203 * @param cArgs Number of arguments following in the ellipsis.
204 * @param ... Function arguments.
205 *
206 * @remarks See remarks on VMR3ReqCallVU.
207 */
208VMMR3DECL(int) VMR3ReqCallNoWaitU(PUVM pUVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
209{
210 va_list va;
211 va_start(va, cArgs);
212 int rc = VMR3ReqCallVU(pUVM, idDstCpu, NULL, 0, VMREQFLAGS_VBOX_STATUS | VMREQFLAGS_NO_WAIT,
213 pfnFunction, cArgs, va);
214 va_end(va);
215 return rc;
216}
217
218
219/**
220 * Convenience wrapper for VMR3ReqCallU.
221 *
222 * This assumes (1) you're calling a function that returns void, and (2) that
223 * you wish to wait for ever for it to return.
224 *
225 * @returns VBox status code of VMR3ReqCallVU.
226 *
227 * @param pVM Pointer to the shared VM structure.
228 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
229 * one of the following special values:
230 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
231 * @param pfnFunction Pointer to the function to call.
232 * @param cArgs Number of arguments following in the ellipsis.
233 * @param ... Function arguments.
234 *
235 * @remarks See remarks on VMR3ReqCallVU.
236 */
237VMMR3DECL(int) VMR3ReqCallVoidWait(PVM pVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
238{
239 PVMREQ pReq;
240 va_list va;
241 va_start(va, cArgs);
242 int rc = VMR3ReqCallVU(pVM->pUVM, idDstCpu, &pReq, RT_INDEFINITE_WAIT, VMREQFLAGS_VOID,
243 pfnFunction, cArgs, va);
244 va_end(va);
245 VMR3ReqFree(pReq);
246 return rc;
247}
248
249
250/**
251 * Convenience wrapper for VMR3ReqCallU.
252 *
253 * This assumes (1) you're calling a function that returns void, and (2) that
254 * you wish to wait for ever for it to return.
255 *
256 * @returns VBox status code of VMR3ReqCallVU.
257 *
258 * @param pUVM Pointer to the user mode VM structure.
259 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
260 * one of the following special values:
261 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
262 * @param pfnFunction Pointer to the function to call.
263 * @param cArgs Number of arguments following in the ellipsis.
264 * @param ... Function arguments.
265 *
266 * @remarks See remarks on VMR3ReqCallVU.
267 */
268VMMR3DECL(int) VMR3ReqCallVoidWaitU(PUVM pUVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
269{
270 PVMREQ pReq;
271 va_list va;
272 va_start(va, cArgs);
273 int rc = VMR3ReqCallVU(pUVM, idDstCpu, &pReq, RT_INDEFINITE_WAIT, VMREQFLAGS_VOID,
274 pfnFunction, cArgs, va);
275 va_end(va);
276 VMR3ReqFree(pReq);
277 return rc;
278}
279
280
281/**
282 * Convenience wrapper for VMR3ReqCallU.
283 *
284 * This assumes (1) you're calling a function that returns void, and (2) that
285 * you do not wish to wait for it to complete.
286 *
287 * @returns VBox status code of VMR3ReqCallVU.
288 *
289 * @param pVM Pointer to the shared VM structure.
290 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
291 * one of the following special values:
292 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
293 * @param pfnFunction Pointer to the function to call.
294 * @param cArgs Number of arguments following in the ellipsis.
295 * @param ... Function arguments.
296 *
297 * @remarks See remarks on VMR3ReqCallVU.
298 */
299VMMR3DECL(int) VMR3ReqCallVoidNoWait(PVM pVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
300{
301 PVMREQ pReq;
302 va_list va;
303 va_start(va, cArgs);
304 int rc = VMR3ReqCallVU(pVM->pUVM, idDstCpu, &pReq, RT_INDEFINITE_WAIT, VMREQFLAGS_VOID | VMREQFLAGS_NO_WAIT,
305 pfnFunction, cArgs, va);
306 va_end(va);
307 VMR3ReqFree(pReq);
308 return rc;
309}
310
311
312/**
313 * Convenience wrapper for VMR3ReqCallU.
314 *
315 * This assumes (1) you're calling a function that returns void, and (2) that
316 * you do not wish to wait for it to complete.
317 *
318 * @returns VBox status code of VMR3ReqCallVU.
319 *
320 * @param pUVM Pointer to the user mode VM structure.
321 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
322 * one of the following special values:
323 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
324 * @param pfnFunction Pointer to the function to call.
325 * @param cArgs Number of arguments following in the ellipsis.
326 * @param ... Function arguments.
327 *
328 * @remarks See remarks on VMR3ReqCallVU.
329 */
330VMMR3DECL(int) VMR3ReqCallVoidNoWaitU(PUVM pUVM, VMCPUID idDstCpu, PFNRT pfnFunction, unsigned cArgs, ...)
331{
332 PVMREQ pReq;
333 va_list va;
334 va_start(va, cArgs);
335 int rc = VMR3ReqCallVU(pUVM, idDstCpu, &pReq, RT_INDEFINITE_WAIT, VMREQFLAGS_VOID | VMREQFLAGS_NO_WAIT,
336 pfnFunction, cArgs, va);
337 va_end(va);
338 VMR3ReqFree(pReq);
339 return rc;
340}
341
342
343/**
344 * Allocate and queue a call request to a void function.
345 *
346 * If it's desired to poll on the completion of the request set cMillies
347 * to 0 and use VMR3ReqWait() to check for completation. In the other case
348 * use RT_INDEFINITE_WAIT.
349 * The returned request packet must be freed using VMR3ReqFree().
350 *
351 * @returns VBox status code.
352 * Will not return VERR_INTERRUPTED.
353 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
354 *
355 * @param pUVM Pointer to the user mode VM structure.
356 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
357 * one of the following special values:
358 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
359 * @param ppReq Where to store the pointer to the request.
360 * This will be NULL or a valid request pointer not matter what happends, unless fFlags
361 * contains VMREQFLAGS_NO_WAIT when it will be optional and always NULL.
362 * @param cMillies Number of milliseconds to wait for the request to
363 * be completed. Use RT_INDEFINITE_WAIT to only
364 * wait till it's completed.
365 * @param fFlags A combination of the VMREQFLAGS values.
366 * @param pfnFunction Pointer to the function to call.
367 * @param cArgs Number of arguments following in the ellipsis.
368 * @param ... Function arguments.
369 *
370 * @remarks See remarks on VMR3ReqCallVU.
371 */
372VMMR3DECL(int) VMR3ReqCallU(PUVM pUVM, VMCPUID idDstCpu, PVMREQ *ppReq, unsigned cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
373{
374 va_list va;
375 va_start(va, cArgs);
376 int rc = VMR3ReqCallVU(pUVM, idDstCpu, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
377 va_end(va);
378 return rc;
379}
380
381
382/**
383 * Allocate and queue a call request.
384 *
385 * If it's desired to poll on the completion of the request set cMillies
386 * to 0 and use VMR3ReqWait() to check for completation. In the other case
387 * use RT_INDEFINITE_WAIT.
388 * The returned request packet must be freed using VMR3ReqFree().
389 *
390 * @returns VBox status code.
391 * Will not return VERR_INTERRUPTED.
392 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
393 *
394 * @param pUVM Pointer to the user mode VM structure.
395 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
396 * one of the following special values:
397 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
398 * @param ppReq Where to store the pointer to the request.
399 * This will be NULL or a valid request pointer not matter what happends, unless fFlags
400 * contains VMREQFLAGS_NO_WAIT when it will be optional and always NULL.
401 * @param cMillies Number of milliseconds to wait for the request to
402 * be completed. Use RT_INDEFINITE_WAIT to only
403 * wait till it's completed.
404 * @param pfnFunction Pointer to the function to call.
405 * @param fFlags A combination of the VMREQFLAGS values.
406 * @param cArgs Number of arguments following in the ellipsis.
407 * Stuff which differs in size from uintptr_t is gonna make trouble, so don't try!
408 * @param Args Argument vector.
409 *
410 * @remarks Caveats:
411 * - Do not pass anything which is larger than an uintptr_t.
412 * - 64-bit integers are larger than uintptr_t on 32-bit hosts.
413 * Pass integers > 32-bit by reference (pointers).
414 * - Don't use NULL since it should be the integer 0 in C++ and may
415 * therefore end up with garbage in the bits 63:32 on 64-bit
416 * hosts because 'int' is 32-bit.
417 * Use (void *)NULL or (uintptr_t)0 instead of NULL.
418 */
419VMMR3DECL(int) VMR3ReqCallVU(PUVM pUVM, VMCPUID idDstCpu, PVMREQ *ppReq, unsigned cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, va_list Args)
420{
421 LogFlow(("VMR3ReqCallV: idDstCpu=%u cMillies=%d fFlags=%#x pfnFunction=%p cArgs=%d\n", idDstCpu, cMillies, fFlags, pfnFunction, cArgs));
422
423 /*
424 * Validate input.
425 */
426 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
427 AssertPtrReturn(pUVM, VERR_INVALID_POINTER);
428 AssertReturn(!(fFlags & ~(VMREQFLAGS_RETURN_MASK | VMREQFLAGS_NO_WAIT | VMREQFLAGS_POKE)), VERR_INVALID_PARAMETER);
429 if (!(fFlags & VMREQFLAGS_NO_WAIT) || ppReq)
430 {
431 AssertPtrReturn(ppReq, VERR_INVALID_POINTER);
432 *ppReq = NULL;
433 }
434 PVMREQ pReq = NULL;
435 AssertMsgReturn(cArgs * sizeof(uintptr_t) <= sizeof(pReq->u.Internal.aArgs),
436 ("cArg=%d\n", cArgs),
437 VERR_TOO_MUCH_DATA);
438
439 /*
440 * Allocate request
441 */
442 int rc = VMR3ReqAllocU(pUVM, &pReq, VMREQTYPE_INTERNAL, idDstCpu);
443 if (RT_FAILURE(rc))
444 return rc;
445
446 /*
447 * Initialize the request data.
448 */
449 pReq->fFlags = fFlags;
450 pReq->u.Internal.pfn = pfnFunction;
451 pReq->u.Internal.cArgs = cArgs;
452 for (unsigned iArg = 0; iArg < cArgs; iArg++)
453 pReq->u.Internal.aArgs[iArg] = va_arg(Args, uintptr_t);
454
455 /*
456 * Queue the request and return.
457 */
458 rc = VMR3ReqQueue(pReq, cMillies);
459 if ( RT_FAILURE(rc)
460 && rc != VERR_TIMEOUT)
461 {
462 VMR3ReqFree(pReq);
463 pReq = NULL;
464 }
465 if (!(fFlags & VMREQFLAGS_NO_WAIT))
466 {
467 *ppReq = pReq;
468 LogFlow(("VMR3ReqCallV: returns %Rrc *ppReq=%p\n", rc, pReq));
469 }
470 else
471 LogFlow(("VMR3ReqCallV: returns %Rrc\n", rc));
472 Assert(rc != VERR_INTERRUPTED);
473 return rc;
474}
475
476
477/**
478 * Joins the list pList with whatever is linked up at *pHead.
479 */
480static void vmr3ReqJoinFreeSub(volatile PVMREQ *ppHead, PVMREQ pList)
481{
482 for (unsigned cIterations = 0;; cIterations++)
483 {
484 PVMREQ pHead = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)ppHead, pList);
485 if (!pHead)
486 return;
487 PVMREQ pTail = pHead;
488 while (pTail->pNext)
489 pTail = pTail->pNext;
490 ASMAtomicWritePtr((void * volatile *)&pTail->pNext, pList);
491 ASMCompilerBarrier();
492 if (ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (void *)pHead, pList))
493 return;
494 ASMAtomicWritePtr((void * volatile *)&pTail->pNext, NULL);
495 ASMCompilerBarrier();
496 if (ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (void *)pHead, NULL))
497 return;
498 pList = pHead;
499 Assert(cIterations != 32);
500 Assert(cIterations != 64);
501 }
502}
503
504
505/**
506 * Joins the list pList with whatever is linked up at *pHead.
507 */
508static void vmr3ReqJoinFree(PVMINTUSERPERVM pVMInt, PVMREQ pList)
509{
510 /*
511 * Split the list if it's too long.
512 */
513 unsigned cReqs = 1;
514 PVMREQ pTail = pList;
515 while (pTail->pNext)
516 {
517 if (cReqs++ > 25)
518 {
519 const uint32_t i = pVMInt->iReqFree;
520 vmr3ReqJoinFreeSub(&pVMInt->apReqFree[(i + 2) % RT_ELEMENTS(pVMInt->apReqFree)], pTail->pNext);
521
522 pTail->pNext = NULL;
523 vmr3ReqJoinFreeSub(&pVMInt->apReqFree[(i + 2 + (i == pVMInt->iReqFree)) % RT_ELEMENTS(pVMInt->apReqFree)], pTail->pNext);
524 return;
525 }
526 pTail = pTail->pNext;
527 }
528 vmr3ReqJoinFreeSub(&pVMInt->apReqFree[(pVMInt->iReqFree + 2) % RT_ELEMENTS(pVMInt->apReqFree)], pList);
529}
530
531
532/**
533 * Allocates a request packet.
534 *
535 * The caller allocates a request packet, fills in the request data
536 * union and queues the request.
537 *
538 * @returns VBox status code.
539 *
540 * @param pVM VM handle.
541 * @param ppReq Where to store the pointer to the allocated packet.
542 * @param enmType Package type.
543 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
544 * one of the following special values:
545 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
546 */
547VMMR3DECL(int) VMR3ReqAlloc(PVM pVM, PVMREQ *ppReq, VMREQTYPE enmType, VMCPUID idDstCpu)
548{
549 return VMR3ReqAllocU(pVM->pUVM, ppReq, enmType, idDstCpu);
550}
551
552
553/**
554 * Allocates a request packet.
555 *
556 * The caller allocates a request packet, fills in the request data
557 * union and queues the request.
558 *
559 * @returns VBox status code.
560 *
561 * @param pUVM Pointer to the user mode VM structure.
562 * @param ppReq Where to store the pointer to the allocated packet.
563 * @param enmType Package type.
564 * @param idDstCpu The destination CPU(s). Either a specific CPU ID or
565 * one of the following special values:
566 * VMCPUID_ANY, VMCPUID_ANY_QUEUE, VMCPUID_ALL or VMCPUID_ALL_REVERSE.
567 */
568VMMR3DECL(int) VMR3ReqAllocU(PUVM pUVM, PVMREQ *ppReq, VMREQTYPE enmType, VMCPUID idDstCpu)
569{
570 /*
571 * Validate input.
572 */
573 AssertMsgReturn(enmType > VMREQTYPE_INVALID && enmType < VMREQTYPE_MAX,
574 ("Invalid package type %d valid range %d-%d inclusivly.\n",
575 enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
576 VERR_VM_REQUEST_INVALID_TYPE);
577 AssertPtrReturn(ppReq, VERR_INVALID_POINTER);
578 AssertMsgReturn( idDstCpu == VMCPUID_ANY
579 || idDstCpu == VMCPUID_ANY_QUEUE
580 || idDstCpu < pUVM->cCpus
581 || idDstCpu == VMCPUID_ALL
582 || idDstCpu == VMCPUID_ALL_REVERSE,
583 ("Invalid destination %u (max=%u)\n", idDstCpu, pUVM->cCpus), VERR_INVALID_PARAMETER);
584
585 /*
586 * Try get a recycled packet.
587 * While this could all be solved with a single list with a lock, it's a sport
588 * of mine to avoid locks.
589 */
590 int cTries = RT_ELEMENTS(pUVM->vm.s.apReqFree) * 2;
591 while (--cTries >= 0)
592 {
593 PVMREQ volatile *ppHead = &pUVM->vm.s.apReqFree[ASMAtomicIncU32(&pUVM->vm.s.iReqFree) % RT_ELEMENTS(pUVM->vm.s.apReqFree)];
594#if 0 /* sad, but this won't work safely because the reading of pReq->pNext. */
595 PVMREQ pNext = NULL;
596 PVMREQ pReq = *ppHead;
597 if ( pReq
598 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (pNext = pReq->pNext), pReq)
599 && (pReq = *ppHead)
600 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (pNext = pReq->pNext), pReq))
601 pReq = NULL;
602 if (pReq)
603 {
604 Assert(pReq->pNext == pNext); NOREF(pReq);
605#else
606 PVMREQ pReq = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)ppHead, NULL);
607 if (pReq)
608 {
609 PVMREQ pNext = pReq->pNext;
610 if ( pNext
611 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, pNext, NULL))
612 {
613 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocRaces);
614 vmr3ReqJoinFree(&pUVM->vm.s, pReq->pNext);
615 }
616#endif
617 ASMAtomicDecU32(&pUVM->vm.s.cReqFree);
618
619 /*
620 * Make sure the event sem is not signaled.
621 */
622 if (!pReq->fEventSemClear)
623 {
624 int rc = RTSemEventWait(pReq->EventSem, 0);
625 if (rc != VINF_SUCCESS && rc != VERR_TIMEOUT)
626 {
627 /*
628 * This shall not happen, but if it does we'll just destroy
629 * the semaphore and create a new one.
630 */
631 AssertMsgFailed(("rc=%Rrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
632 RTSemEventDestroy(pReq->EventSem);
633 rc = RTSemEventCreate(&pReq->EventSem);
634 AssertRC(rc);
635 if (RT_FAILURE(rc))
636 return rc;
637 }
638 pReq->fEventSemClear = true;
639 }
640 else
641 Assert(RTSemEventWait(pReq->EventSem, 0) == VERR_TIMEOUT);
642
643 /*
644 * Initialize the packet and return it.
645 */
646 Assert(pReq->enmType == VMREQTYPE_INVALID);
647 Assert(pReq->enmState == VMREQSTATE_FREE);
648 Assert(pReq->pUVM == pUVM);
649 ASMAtomicXchgSize(&pReq->pNext, NULL);
650 pReq->enmState = VMREQSTATE_ALLOCATED;
651 pReq->iStatus = VERR_VM_REQUEST_STATUS_STILL_PENDING;
652 pReq->fFlags = VMREQFLAGS_VBOX_STATUS;
653 pReq->enmType = enmType;
654 pReq->idDstCpu = idDstCpu;
655
656 *ppReq = pReq;
657 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocRecycled);
658 LogFlow(("VMR3ReqAlloc: returns VINF_SUCCESS *ppReq=%p recycled\n", pReq));
659 return VINF_SUCCESS;
660 }
661 }
662
663 /*
664 * Ok allocate one.
665 */
666 PVMREQ pReq = (PVMREQ)MMR3HeapAllocU(pUVM, MM_TAG_VM_REQ, sizeof(*pReq));
667 if (!pReq)
668 return VERR_NO_MEMORY;
669
670 /*
671 * Create the semaphore.
672 */
673 int rc = RTSemEventCreate(&pReq->EventSem);
674 AssertRC(rc);
675 if (RT_FAILURE(rc))
676 {
677 MMR3HeapFree(pReq);
678 return rc;
679 }
680
681 /*
682 * Initialize the packet and return it.
683 */
684 pReq->pNext = NULL;
685 pReq->pUVM = pUVM;
686 pReq->enmState = VMREQSTATE_ALLOCATED;
687 pReq->iStatus = VERR_VM_REQUEST_STATUS_STILL_PENDING;
688 pReq->fEventSemClear = true;
689 pReq->fFlags = VMREQFLAGS_VBOX_STATUS;
690 pReq->enmType = enmType;
691 pReq->idDstCpu = idDstCpu;
692
693 *ppReq = pReq;
694 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocNew);
695 LogFlow(("VMR3ReqAlloc: returns VINF_SUCCESS *ppReq=%p new\n", pReq));
696 return VINF_SUCCESS;
697}
698
699
700/**
701 * Free a request packet.
702 *
703 * @returns VBox status code.
704 *
705 * @param pReq Package to free.
706 * @remark The request packet must be in allocated or completed state!
707 */
708VMMR3DECL(int) VMR3ReqFree(PVMREQ pReq)
709{
710 /*
711 * Ignore NULL (all free functions should do this imho).
712 */
713 if (!pReq)
714 return VINF_SUCCESS;
715
716 /*
717 * Check packet state.
718 */
719 switch (pReq->enmState)
720 {
721 case VMREQSTATE_ALLOCATED:
722 case VMREQSTATE_COMPLETED:
723 break;
724 default:
725 AssertMsgFailed(("Invalid state %d!\n", pReq->enmState));
726 return VERR_VM_REQUEST_STATE;
727 }
728
729 /*
730 * Make it a free packet and put it into one of the free packet lists.
731 */
732 pReq->enmState = VMREQSTATE_FREE;
733 pReq->iStatus = VERR_VM_REQUEST_STATUS_FREED;
734 pReq->enmType = VMREQTYPE_INVALID;
735
736 PUVM pUVM = pReq->pUVM;
737 STAM_COUNTER_INC(&pUVM->vm.s.StatReqFree);
738
739 if (pUVM->vm.s.cReqFree < 128)
740 {
741 ASMAtomicIncU32(&pUVM->vm.s.cReqFree);
742 PVMREQ volatile *ppHead = &pUVM->vm.s.apReqFree[ASMAtomicIncU32(&pUVM->vm.s.iReqFree) % RT_ELEMENTS(pUVM->vm.s.apReqFree)];
743 PVMREQ pNext;
744 do
745 {
746 pNext = (PVMREQ)ASMAtomicUoReadPtr((void * volatile *)ppHead);
747 ASMAtomicWritePtr((void * volatile *)&pReq->pNext, pNext);
748 ASMCompilerBarrier();
749 } while (!ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (void *)pReq, (void *)pNext));
750 }
751 else
752 {
753 STAM_COUNTER_INC(&pReq->pUVM->vm.s.StatReqFreeOverflow);
754 RTSemEventDestroy(pReq->EventSem);
755 MMR3HeapFree(pReq);
756 }
757 return VINF_SUCCESS;
758}
759
760
761/**
762 * Queue a request.
763 *
764 * The quest must be allocated using VMR3ReqAlloc() and contain
765 * all the required data.
766 * If it's desired to poll on the completion of the request set cMillies
767 * to 0 and use VMR3ReqWait() to check for completation. In the other case
768 * use RT_INDEFINITE_WAIT.
769 *
770 * @returns VBox status code.
771 * Will not return VERR_INTERRUPTED.
772 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
773 *
774 * @param pReq The request to queue.
775 * @param cMillies Number of milliseconds to wait for the request to
776 * be completed. Use RT_INDEFINITE_WAIT to only
777 * wait till it's completed.
778 */
779VMMR3DECL(int) VMR3ReqQueue(PVMREQ pReq, unsigned cMillies)
780{
781 LogFlow(("VMR3ReqQueue: pReq=%p cMillies=%d\n", pReq, cMillies));
782 /*
783 * Verify the supplied package.
784 */
785 AssertMsgReturn(pReq->enmState == VMREQSTATE_ALLOCATED, ("%d\n", pReq->enmState), VERR_VM_REQUEST_STATE);
786 AssertMsgReturn( VALID_PTR(pReq->pUVM)
787 && !pReq->pNext
788 && pReq->EventSem != NIL_RTSEMEVENT,
789 ("Invalid request package! Anyone cooking their own packages???\n"),
790 VERR_VM_REQUEST_INVALID_PACKAGE);
791 AssertMsgReturn( pReq->enmType > VMREQTYPE_INVALID
792 && pReq->enmType < VMREQTYPE_MAX,
793 ("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc too...\n",
794 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
795 VERR_VM_REQUEST_INVALID_TYPE);
796 Assert(!(pReq->fFlags & ~(VMREQFLAGS_RETURN_MASK | VMREQFLAGS_NO_WAIT | VMREQFLAGS_POKE)));
797
798 /*
799 * Are we the EMT or not?
800 * Also, store pVM (and fFlags) locally since pReq may be invalid after queuing it.
801 */
802 int rc = VINF_SUCCESS;
803 PUVM pUVM = ((VMREQ volatile *)pReq)->pUVM; /* volatile paranoia */
804 PUVMCPU pUVCpu = (PUVMCPU)RTTlsGet(pUVM->vm.s.idxTLS);
805
806 if (pReq->idDstCpu == VMCPUID_ALL)
807 {
808 /* One-by-one. */
809 Assert(!(pReq->fFlags & VMREQFLAGS_NO_WAIT));
810 for (unsigned i = 0; i < pUVM->cCpus; i++)
811 {
812 /* Reinit some members. */
813 pReq->enmState = VMREQSTATE_ALLOCATED;
814 pReq->idDstCpu = i;
815 rc = VMR3ReqQueue(pReq, cMillies);
816 if (RT_FAILURE(rc))
817 break;
818 }
819 }
820 else if (pReq->idDstCpu == VMCPUID_ALL_REVERSE)
821 {
822 /* One-by-one. */
823 Assert(!(pReq->fFlags & VMREQFLAGS_NO_WAIT));
824 for (int i = pUVM->cCpus-1; i >= 0; i--)
825 {
826 /* Reinit some members. */
827 pReq->enmState = VMREQSTATE_ALLOCATED;
828 pReq->idDstCpu = i;
829 rc = VMR3ReqQueue(pReq, cMillies);
830 if (RT_FAILURE(rc))
831 break;
832 }
833 }
834 else if ( pReq->idDstCpu != VMCPUID_ANY /* for a specific VMCPU? */
835 && pReq->idDstCpu != VMCPUID_ANY_QUEUE
836 && ( !pUVCpu /* and it's not the current thread. */
837 || pUVCpu->idCpu != pReq->idDstCpu))
838 {
839 VMCPUID idTarget = pReq->idDstCpu; Assert(idTarget < pUVM->cCpus);
840 PVMCPU pVCpu = &pUVM->pVM->aCpus[idTarget];
841 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
842
843 /* Fetch the right UVMCPU */
844 pUVCpu = &pUVM->aCpus[idTarget];
845
846 /*
847 * Insert it.
848 */
849 pReq->enmState = VMREQSTATE_QUEUED;
850 PVMREQ pNext;
851 do
852 {
853 pNext = (PVMREQ)ASMAtomicUoReadPtr((void * volatile *)&pUVCpu->vm.s.pReqs);
854 ASMAtomicWritePtr((void * volatile *)&pReq->pNext, pNext);
855 ASMCompilerBarrier();
856 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVCpu->vm.s.pReqs, (void *)pReq, (void *)pNext));
857
858 /*
859 * Notify EMT.
860 */
861 if (pUVM->pVM)
862 VMCPU_FF_SET(pVCpu, VMCPU_FF_REQUEST);
863 VMR3NotifyCpuFFU(pUVCpu, fFlags & VMREQFLAGS_POKE ? VMNOTIFYFF_FLAGS_POKE : 0);
864
865 /*
866 * Wait and return.
867 */
868 if (!(fFlags & VMREQFLAGS_NO_WAIT))
869 rc = VMR3ReqWait(pReq, cMillies);
870 LogFlow(("VMR3ReqQueue: returns %Rrc\n", rc));
871 }
872 else if ( ( pReq->idDstCpu == VMCPUID_ANY
873 && !pUVCpu /* only EMT threads have a valid pointer stored in the TLS slot. */)
874 || pReq->idDstCpu == VMCPUID_ANY_QUEUE)
875 {
876 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
877
878 Assert(pReq->idDstCpu != VMCPUID_ANY_QUEUE || pUVCpu);
879
880 /*
881 * Insert it.
882 */
883 pReq->enmState = VMREQSTATE_QUEUED;
884 PVMREQ pNext;
885 do
886 {
887 pNext = (PVMREQ)ASMAtomicUoReadPtr((void * volatile *)&pUVM->vm.s.pReqs);
888 ASMAtomicWritePtr((void * volatile *)&pReq->pNext, pNext);
889 ASMCompilerBarrier();
890 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->vm.s.pReqs, (void *)pReq, (void *)pNext));
891
892 /*
893 * Notify EMT.
894 */
895 if (pUVM->pVM)
896 VM_FF_SET(pUVM->pVM, VM_FF_REQUEST);
897 VMR3NotifyGlobalFFU(pUVM, fFlags & VMREQFLAGS_POKE ? VMNOTIFYFF_FLAGS_POKE : 0);
898
899 /*
900 * Wait and return.
901 */
902 if (!(fFlags & VMREQFLAGS_NO_WAIT))
903 rc = VMR3ReqWait(pReq, cMillies);
904 LogFlow(("VMR3ReqQueue: returns %Rrc\n", rc));
905 }
906 else
907 {
908 Assert(pUVCpu);
909
910 /*
911 * The requester was an EMT, just execute it.
912 */
913 pReq->enmState = VMREQSTATE_QUEUED;
914 rc = vmR3ReqProcessOneU(pUVM, pReq);
915 LogFlow(("VMR3ReqQueue: returns %Rrc (processed)\n", rc));
916 }
917 return rc;
918}
919
920
921/**
922 * Wait for a request to be completed.
923 *
924 * @returns VBox status code.
925 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
926 *
927 * @param pReq The request to wait for.
928 * @param cMillies Number of milliseconds to wait.
929 * Use RT_INDEFINITE_WAIT to only wait till it's completed.
930 */
931VMMR3DECL(int) VMR3ReqWait(PVMREQ pReq, unsigned cMillies)
932{
933 LogFlow(("VMR3ReqWait: pReq=%p cMillies=%d\n", pReq, cMillies));
934
935 /*
936 * Verify the supplied package.
937 */
938 AssertMsgReturn( pReq->enmState == VMREQSTATE_QUEUED
939 || pReq->enmState == VMREQSTATE_PROCESSING
940 || pReq->enmState == VMREQSTATE_COMPLETED,
941 ("Invalid state %d\n", pReq->enmState),
942 VERR_VM_REQUEST_STATE);
943 AssertMsgReturn( VALID_PTR(pReq->pUVM)
944 && pReq->EventSem != NIL_RTSEMEVENT,
945 ("Invalid request package! Anyone cooking their own packages???\n"),
946 VERR_VM_REQUEST_INVALID_PACKAGE);
947 AssertMsgReturn( pReq->enmType > VMREQTYPE_INVALID
948 && pReq->enmType < VMREQTYPE_MAX,
949 ("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc too...\n",
950 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
951 VERR_VM_REQUEST_INVALID_TYPE);
952
953 /*
954 * Check for deadlock condition
955 */
956 PUVM pUVM = pReq->pUVM;
957 NOREF(pUVM);
958
959 /*
960 * Wait on the package.
961 */
962 int rc;
963 if (cMillies != RT_INDEFINITE_WAIT)
964 rc = RTSemEventWait(pReq->EventSem, cMillies);
965 else
966 {
967 do
968 {
969 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
970 Assert(rc != VERR_TIMEOUT);
971 } while ( pReq->enmState != VMREQSTATE_COMPLETED
972 && pReq->enmState != VMREQSTATE_INVALID);
973 }
974 if (RT_SUCCESS(rc))
975 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
976 if (pReq->enmState == VMREQSTATE_COMPLETED)
977 rc = VINF_SUCCESS;
978 LogFlow(("VMR3ReqWait: returns %Rrc\n", rc));
979 Assert(rc != VERR_INTERRUPTED);
980 return rc;
981}
982
983
984/**
985 * Process pending request(s).
986 *
987 * This function is called from a forced action handler in the EMT
988 * or from one of the EMT loops.
989 *
990 * @returns VBox status code.
991 *
992 * @param pUVM Pointer to the user mode VM structure.
993 * @param idDstCpu Pass VMCPUID_ANY to process the common request queue
994 * and the CPU ID for a CPU specific one. In the latter
995 * case the calling thread must be the EMT of that CPU.
996 *
997 * @note SMP safe (multiple EMTs trying to satisfy VM_FF_REQUESTs).
998 */
999VMMR3DECL(int) VMR3ReqProcessU(PUVM pUVM, VMCPUID idDstCpu)
1000{
1001 LogFlow(("VMR3ReqProcessU: (enmVMState=%d) idDstCpu=%d\n", pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING, idDstCpu));
1002
1003 /*
1004 * Process loop.
1005 *
1006 * We do not repeat the outer loop if we've got an informational status code
1007 * since that code needs processing by our caller.
1008 */
1009 int rc = VINF_SUCCESS;
1010 while (rc <= VINF_SUCCESS)
1011 {
1012 /*
1013 * Get pending requests.
1014 */
1015 void * volatile *ppReqs;
1016 if (idDstCpu == VMCPUID_ANY)
1017 {
1018 ppReqs = (void * volatile *)&pUVM->vm.s.pReqs;
1019 if (RT_LIKELY(pUVM->pVM))
1020 VM_FF_CLEAR(pUVM->pVM, VM_FF_REQUEST);
1021 }
1022 else
1023 {
1024 Assert(idDstCpu < pUVM->cCpus);
1025 Assert(pUVM->aCpus[idDstCpu].vm.s.NativeThreadEMT == RTThreadNativeSelf());
1026 ppReqs = (void * volatile *)&pUVM->aCpus[idDstCpu].vm.s.pReqs;
1027 if (RT_LIKELY(pUVM->pVM))
1028 VMCPU_FF_CLEAR(&pUVM->pVM->aCpus[idDstCpu], VMCPU_FF_REQUEST);
1029 }
1030 PVMREQ pReqs = (PVMREQ)ASMAtomicXchgPtr(ppReqs, NULL);
1031 if (!pReqs)
1032 break;
1033
1034 /*
1035 * Reverse the list to process it in FIFO order.
1036 */
1037 PVMREQ pReq = pReqs;
1038 if (pReq->pNext)
1039 Log2(("VMR3ReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
1040 pReqs = NULL;
1041 while (pReq)
1042 {
1043 Assert(pReq->enmState == VMREQSTATE_QUEUED);
1044 Assert(pReq->pUVM == pUVM);
1045 PVMREQ pCur = pReq;
1046 pReq = pReq->pNext;
1047 pCur->pNext = pReqs;
1048 pReqs = pCur;
1049 }
1050
1051
1052 /*
1053 * Process the requests.
1054 *
1055 * Since this is a FF worker certain rules applies to the
1056 * status codes. See the EM section in VBox/err.h and EM.cpp for details.
1057 */
1058 while (pReqs)
1059 {
1060 /* Unchain the first request and advance the list. */
1061 pReq = pReqs;
1062 pReqs = pReqs->pNext;
1063 pReq->pNext = NULL;
1064
1065 /* Process the request */
1066 int rc2 = vmR3ReqProcessOneU(pUVM, pReq);
1067
1068 /*
1069 * The status code handling extremely important yet very fragile. Should probably
1070 * look for a better way of communicating status changes to EM...
1071 */
1072 if ( rc2 >= VINF_EM_FIRST
1073 && rc2 <= VINF_EM_LAST
1074 && ( rc == VINF_SUCCESS
1075 || rc2 < rc) )
1076 rc = rc2;
1077 /** @todo may have to abort processing to propagate EM scheduling status codes
1078 * up to the caller... See the ugly hacks after VMMR3EmtRendezvousFF
1079 * and VMR3ReqProcessU in EM.cpp. */
1080 }
1081 }
1082
1083 LogFlow(("VMR3ReqProcess: returns %Rrc (enmVMState=%d)\n", rc, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING));
1084 return rc;
1085}
1086
1087
1088/**
1089 * Process one request.
1090 *
1091 * @returns VBox status code.
1092 *
1093 * @param pVM VM handle.
1094 * @param pReq Request packet to process.
1095 */
1096static int vmR3ReqProcessOneU(PUVM pUVM, PVMREQ pReq)
1097{
1098 LogFlow(("vmR3ReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
1099
1100 /*
1101 * Process the request.
1102 */
1103 Assert(pReq->enmState == VMREQSTATE_QUEUED);
1104 pReq->enmState = VMREQSTATE_PROCESSING;
1105 int rcRet = VINF_SUCCESS; /* the return code of this function. */
1106 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
1107 switch (pReq->enmType)
1108 {
1109 /*
1110 * A packed down call frame.
1111 */
1112 case VMREQTYPE_INTERNAL:
1113 {
1114 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
1115 union
1116 {
1117 PFNRT pfn;
1118 DECLCALLBACKMEMBER(int, pfn00)(void);
1119 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
1120 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
1121 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
1122 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1123 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1124 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1125 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1126 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1127 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1128 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1129 DECLCALLBACKMEMBER(int, pfn11)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1130 DECLCALLBACKMEMBER(int, pfn12)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
1131 } u;
1132 u.pfn = pReq->u.Internal.pfn;
1133#ifdef RT_ARCH_AMD64
1134 switch (pReq->u.Internal.cArgs)
1135 {
1136 case 0: rcRet = u.pfn00(); break;
1137 case 1: rcRet = u.pfn01(pauArgs[0]); break;
1138 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
1139 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
1140 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
1141 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
1142 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
1143 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
1144 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
1145 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
1146 case 10: rcRet = u.pfn10(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9]); break;
1147 case 11: rcRet = u.pfn11(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10]); break;
1148 case 12: rcRet = u.pfn12(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10], pauArgs[11]); break;
1149 default:
1150 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
1151 rcRet = rcReq = VERR_INTERNAL_ERROR;
1152 break;
1153 }
1154#else /* x86: */
1155 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
1156# ifdef __GNUC__
1157 __asm__ __volatile__("movl %%esp, %%edx\n\t"
1158 "subl %2, %%esp\n\t"
1159 "andl $0xfffffff0, %%esp\n\t"
1160 "shrl $2, %2\n\t"
1161 "movl %%esp, %%edi\n\t"
1162 "rep movsl\n\t"
1163 "movl %%edx, %%edi\n\t"
1164 "call *%%eax\n\t"
1165 "mov %%edi, %%esp\n\t"
1166 : "=a" (rcRet),
1167 "=S" (pauArgs),
1168 "=c" (cbArgs)
1169 : "0" (u.pfn),
1170 "1" (pauArgs),
1171 "2" (cbArgs)
1172 : "edi", "edx");
1173# else
1174 __asm
1175 {
1176 xor edx, edx /* just mess it up. */
1177 mov eax, u.pfn
1178 mov ecx, cbArgs
1179 shr ecx, 2
1180 mov esi, pauArgs
1181 mov ebx, esp
1182 sub esp, cbArgs
1183 and esp, 0xfffffff0
1184 mov edi, esp
1185 rep movsd
1186 call eax
1187 mov esp, ebx
1188 mov rcRet, eax
1189 }
1190# endif
1191#endif /* x86 */
1192 if ((pReq->fFlags & (VMREQFLAGS_RETURN_MASK)) == VMREQFLAGS_VOID)
1193 rcRet = VINF_SUCCESS;
1194 rcReq = rcRet;
1195 break;
1196 }
1197
1198 default:
1199 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
1200 rcReq = VERR_NOT_IMPLEMENTED;
1201 break;
1202 }
1203
1204 /*
1205 * Complete the request.
1206 */
1207 pReq->iStatus = rcReq;
1208 pReq->enmState = VMREQSTATE_COMPLETED;
1209 if (pReq->fFlags & VMREQFLAGS_NO_WAIT)
1210 {
1211 /* Free the packet, nobody is waiting. */
1212 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - freeing it\n",
1213 pReq, rcReq, rcRet));
1214 VMR3ReqFree(pReq);
1215 }
1216 else
1217 {
1218 /* Notify the waiter and him free up the packet. */
1219 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
1220 pReq, rcReq, rcRet));
1221 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
1222 int rc2 = RTSemEventSignal(pReq->EventSem);
1223 if (RT_FAILURE(rc2))
1224 {
1225 AssertRC(rc2);
1226 rcRet = rc2;
1227 }
1228 }
1229 return rcRet;
1230}
1231
1232
1233
1234
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