/********************************************************* * 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 # include #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; }