277 lines
8.0 KiB
C
277 lines
8.0 KiB
C
/*********************************************************
|
|
* Copyright (C) 1998-2017 VMware, Inc. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation version 2 and no later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*********************************************************/
|
|
|
|
/*
|
|
* moduleloop.c --
|
|
*
|
|
* Platform independent routines, private to VMCORE, to
|
|
* support module calls and user calls in the module.
|
|
*
|
|
*/
|
|
|
|
#if defined(linux)
|
|
/* Must come before any kernel header file */
|
|
# include "driver-config.h"
|
|
# include <linux/kernel.h>
|
|
# include <linux/sched.h>
|
|
#endif
|
|
#include "vmware.h"
|
|
#include "modulecall.h"
|
|
#include "vmx86.h"
|
|
#include "task.h"
|
|
#include "initblock.h"
|
|
#include "vm_basic_asm.h"
|
|
#include "iocontrols.h"
|
|
#include "hostif.h"
|
|
#include "memtrack.h"
|
|
#include "driver_vmcore.h"
|
|
#include "usercalldefs.h"
|
|
#include "cpuid.h"
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
*
|
|
* Vmx86_RunVM --
|
|
*
|
|
* Main interaction between the module and the monitor:
|
|
*
|
|
* Run the monitor
|
|
* Process module calls from the monitor
|
|
* Make cross user calls to the main thread
|
|
* Return to userlevel to process normal user calls
|
|
* and to signal timeout or errors.
|
|
*
|
|
* Results:
|
|
* Positive: user call number.
|
|
* USERCALL_RESTART: (Linux only)
|
|
* USERCALL_VMX86ALLOCERR: error (message already output)
|
|
*
|
|
* Side effects:
|
|
* Not really, just a switch to monitor and back, that's all.
|
|
*
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
Vmx86_RunVM(VMDriver *vm, // IN:
|
|
Vcpuid vcpuid) // IN:
|
|
{
|
|
uint32 retval = MODULECALL_USERRETURN;
|
|
VMCrossPageData *crosspage = &vm->crosspage[vcpuid]->crosspageData;
|
|
int bailValue = 0;
|
|
|
|
ASSERT(crosspage && CPUID_HostSupportsHV());
|
|
|
|
/*
|
|
* Check if we were interrupted by signal.
|
|
*/
|
|
if (crosspage->moduleCallInterrupted) {
|
|
crosspage->moduleCallInterrupted = FALSE;
|
|
goto skipTaskSwitch;
|
|
}
|
|
|
|
for (;;) {
|
|
/*
|
|
* Task_Switch changes the world to the monitor.
|
|
* The monitor is waiting in the BackToHost routine.
|
|
*/
|
|
UCTIMESTAMP(crosspage, SWITCHING_TO_MONITOR);
|
|
Task_Switch(vm, vcpuid);
|
|
UCTIMESTAMP(crosspage, SWITCHED_TO_MODULE);
|
|
|
|
/*
|
|
* Wake up anything that was waiting for this vcpu to run
|
|
*/
|
|
|
|
if ((VCPUSet_IsEmpty(&crosspage->yieldVCPUs) &&
|
|
crosspage->moduleCallType != MODULECALL_COSCHED) ||
|
|
crosspage->moduleCallType == MODULECALL_SEMAWAIT) {
|
|
HostIF_WakeUpYielders(vm, vcpuid);
|
|
}
|
|
|
|
if (!VCPUSet_IsEmpty(&crosspage->yieldVCPUs) &&
|
|
crosspage->moduleCallType != MODULECALL_COSCHED &&
|
|
crosspage->moduleCallType != MODULECALL_SEMAWAIT) {
|
|
Vmx86_YieldToSet(vm, vcpuid, &crosspage->yieldVCPUs, 0, TRUE);
|
|
}
|
|
|
|
skipTaskSwitch:;
|
|
|
|
retval = MODULECALL_USERRETURN;
|
|
|
|
if (crosspage->userCallType != MODULECALL_USERCALL_NONE) {
|
|
/*
|
|
* This is the main user call path. Handle by returning
|
|
* from the ioctl (back to the userlevel side of a VCPU thread).
|
|
*/
|
|
bailValue = crosspage->userCallType;
|
|
crosspage->retval = retval;
|
|
goto bailOut;
|
|
}
|
|
|
|
switch (crosspage->moduleCallType) {
|
|
case MODULECALL_NONE:
|
|
break;
|
|
|
|
case MODULECALL_INTR: // Already done in task.c
|
|
break;
|
|
|
|
case MODULECALL_GET_RECYCLED_PAGES: {
|
|
MPN mpns[MODULECALL_NUM_ARGS];
|
|
int nPages = MIN((int)crosspage->args[0], MODULECALL_NUM_ARGS);
|
|
|
|
retval = Vmx86_AllocLockedPages(vm, PtrToVA64(mpns), nPages, TRUE,
|
|
FALSE);
|
|
if (retval <= nPages) {
|
|
int i;
|
|
for (i = 0; i < retval; i++) {
|
|
crosspage->args[i] = mpns[i];
|
|
}
|
|
} else {
|
|
// retval is holding an error code
|
|
Warning("Failed to alloc %u pages: %d\n", nPages, (int)retval);
|
|
retval = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_SEMAWAIT: {
|
|
retval = HostIF_SemaphoreWait(vm, vcpuid, crosspage->args);
|
|
|
|
if (retval == MX_WAITINTERRUPTED) {
|
|
crosspage->moduleCallInterrupted = TRUE;
|
|
bailValue = USERCALL_RESTART;
|
|
goto bailOut;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_SEMASIGNAL: {
|
|
retval = HostIF_SemaphoreSignal(crosspage->args);
|
|
|
|
if (retval == MX_WAITINTERRUPTED) {
|
|
crosspage->moduleCallInterrupted = TRUE;
|
|
bailValue = USERCALL_RESTART;
|
|
goto bailOut;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_SEMAFORCEWAKEUP: {
|
|
HostIF_SemaphoreForceWakeup(vm, &crosspage->vcpuSet);
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_IPI: {
|
|
HostIFIPIMode mode;
|
|
mode = HostIF_IPI(vm, &crosspage->vcpuSet);
|
|
retval = (mode != IPI_NONE);
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_RELEASE_ANON_PAGES: {
|
|
unsigned count;
|
|
MPN mpns[MODULECALL_NUM_ARGS];
|
|
for (count = 0; count < MODULECALL_NUM_ARGS; count++) {
|
|
mpns[count] = (MPN)crosspage->args[count];
|
|
if (mpns[count] == INVALID_MPN) {
|
|
break;
|
|
}
|
|
}
|
|
ASSERT(count > 0);
|
|
retval = Vmx86_FreeLockedPages(vm, PtrToVA64(mpns), count, TRUE);
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_LOOKUP_MPN: {
|
|
int i;
|
|
VPN64 vpn = (VPN64)crosspage->args[0];
|
|
uint32 nPages = (uint32)crosspage->args[1];
|
|
VA64 uAddr = (VA64)VPN_2_VA(vpn);
|
|
ASSERT(nPages <= MODULECALL_NUM_ARGS);
|
|
HostIF_VMLock(vm, 38);
|
|
for (i = 0; i < nPages; i++) {
|
|
MPN mpn;
|
|
HostIF_LookupUserMPN(vm, uAddr + i * PAGE_SIZE, &mpn);
|
|
crosspage->args[i] = mpn;
|
|
}
|
|
HostIF_VMUnlock(vm, 38);
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_PIN_MPN: {
|
|
MPN mpn;
|
|
VPN64 vpn = crosspage->args[0];
|
|
VA64 va = VPN_2_VA(vpn);
|
|
retval = Vmx86_LockPage(vm, va, FALSE, &mpn);
|
|
crosspage->args[0] = mpn;
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_COSCHED: {
|
|
uint32 spinUS = (uint32)crosspage->args[0];
|
|
ASSERT_ON_COMPILE(sizeof(uint32) <= sizeof(crosspage->args));
|
|
Vmx86_YieldToSet(vm, vcpuid, &crosspage->vcpuSet, spinUS, FALSE);
|
|
break;
|
|
}
|
|
|
|
case MODULECALL_ALLOC_VMX_PAGE: {
|
|
if (Task_GetHVRootPageForPCPU(crosspage->pcpuNum) == INVALID_MPN) {
|
|
bailValue = USERCALL_VMX86ALLOCERR;
|
|
goto bailOut;
|
|
}
|
|
|
|
retval = crosspage->retval;
|
|
} break;
|
|
|
|
case MODULECALL_ALLOC_TMP_GDT: {
|
|
if (!Task_GetTmpGDT(crosspage->pcpuNum)) {
|
|
bailValue = USERCALL_VMX86ALLOCERR;
|
|
goto bailOut;
|
|
}
|
|
|
|
retval = crosspage->retval;
|
|
} break;
|
|
|
|
case MODULECALL_VMCLEAR_VMCS_ALL_CPUS: {
|
|
MA vmcs = (MA)crosspage->args[0];
|
|
Vmx86_FlushVMCSAllCPUs(vmcs);
|
|
} break;
|
|
|
|
case MODULECALL_GET_PAGE_ROOT: {
|
|
MPN mpn;
|
|
Vcpuid targetVcpuid = (Vcpuid)crosspage->args[0];
|
|
retval = Vmx86_GetPageRoot(vm, targetVcpuid, &mpn);
|
|
crosspage->args[0] = mpn;
|
|
} break;
|
|
|
|
default:
|
|
Warning("ModuleCall %d not supported\n", crosspage->moduleCallType);
|
|
}
|
|
|
|
crosspage->retval = retval;
|
|
|
|
#if defined(linux)
|
|
cond_resched(); // Other kernels are preemptable
|
|
#endif
|
|
}
|
|
|
|
bailOut:
|
|
return bailValue;
|
|
}
|