[4971] | 1 | /* $Id: GVMMR0.cpp 100927 2023-08-21 19:42:52Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[5026] | 3 | * GVMM - Global VM Manager.
|
---|
[4971] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2007-2023 Oracle and/or its affiliates.
|
---|
[4971] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
| 11 | *
|
---|
| 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
| 25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
[4971] | 26 | */
|
---|
| 27 |
|
---|
| 28 |
|
---|
[13001] | 29 | /** @page pg_gvmm GVMM - The Global VM Manager
|
---|
[4971] | 30 | *
|
---|
[32798] | 31 | * The Global VM Manager lives in ring-0. Its main function at the moment is
|
---|
| 32 | * to manage a list of all running VMs, keep a ring-0 only structure (GVM) for
|
---|
| 33 | * each of them, and assign them unique identifiers (so GMM can track page
|
---|
[41783] | 34 | * owners). The GVMM also manage some of the host CPU resources, like the
|
---|
[32798] | 35 | * periodic preemption timer.
|
---|
[4971] | 36 | *
|
---|
[32798] | 37 | * The GVMM will create a ring-0 object for each VM when it is registered, this
|
---|
| 38 | * is both for session cleanup purposes and for having a point where it is
|
---|
| 39 | * possible to implement usage polices later (in SUPR0ObjRegister).
|
---|
| 40 | *
|
---|
| 41 | *
|
---|
[32799] | 42 | * @section sec_gvmm_ppt Periodic Preemption Timer (PPT)
|
---|
[32798] | 43 | *
|
---|
| 44 | * On system that sports a high resolution kernel timer API, we use per-cpu
|
---|
| 45 | * timers to generate interrupts that preempts VT-x, AMD-V and raw-mode guest
|
---|
| 46 | * execution. The timer frequency is calculating by taking the max
|
---|
| 47 | * TMCalcHostTimerFrequency for all VMs running on a CPU for the last ~160 ms
|
---|
| 48 | * (RT_ELEMENTS((PGVMMHOSTCPU)0, Ppt.aHzHistory) *
|
---|
| 49 | * GVMMHOSTCPU_PPT_HIST_INTERVAL_NS).
|
---|
| 50 | *
|
---|
| 51 | * The TMCalcHostTimerFrequency() part of the things gets its takes the max
|
---|
| 52 | * TMTimerSetFrequencyHint() value and adjusts by the current catch-up percent,
|
---|
| 53 | * warp drive percent and some fudge factors. VMMR0.cpp reports the result via
|
---|
| 54 | * GVMMR0SchedUpdatePeriodicPreemptionTimer() before switching to the VT-x,
|
---|
| 55 | * AMD-V and raw-mode execution environments.
|
---|
[4971] | 56 | */
|
---|
| 57 |
|
---|
| 58 |
|
---|
[57358] | 59 | /*********************************************************************************************************************************
|
---|
| 60 | * Header Files *
|
---|
| 61 | *********************************************************************************************************************************/
|
---|
[5031] | 62 | #define LOG_GROUP LOG_GROUP_GVMM
|
---|
[35346] | 63 | #include <VBox/vmm/gvmm.h>
|
---|
| 64 | #include <VBox/vmm/gmm.h>
|
---|
[5031] | 65 | #include "GVMMR0Internal.h"
|
---|
[86683] | 66 | #include <VBox/vmm/dbgf.h>
|
---|
[80641] | 67 | #include <VBox/vmm/iom.h>
|
---|
[80531] | 68 | #include <VBox/vmm/pdm.h>
|
---|
[82556] | 69 | #include <VBox/vmm/pgm.h>
|
---|
[35346] | 70 | #include <VBox/vmm/vmm.h>
|
---|
[71075] | 71 | #ifdef VBOX_WITH_NEM_R0
|
---|
| 72 | # include <VBox/vmm/nem.h>
|
---|
| 73 | #endif
|
---|
[80641] | 74 | #include <VBox/vmm/vmcpuset.h>
|
---|
| 75 | #include <VBox/vmm/vmcc.h>
|
---|
[19398] | 76 | #include <VBox/param.h>
|
---|
[4971] | 77 | #include <VBox/err.h>
|
---|
[40595] | 78 |
|
---|
[29250] | 79 | #include <iprt/asm.h>
|
---|
| 80 | #include <iprt/asm-amd64-x86.h>
|
---|
[58830] | 81 | #include <iprt/critsect.h>
|
---|
[29250] | 82 | #include <iprt/mem.h>
|
---|
[4971] | 83 | #include <iprt/semaphore.h>
|
---|
[5167] | 84 | #include <iprt/time.h>
|
---|
[5031] | 85 | #include <VBox/log.h>
|
---|
[4971] | 86 | #include <iprt/thread.h>
|
---|
[19398] | 87 | #include <iprt/process.h>
|
---|
[4971] | 88 | #include <iprt/param.h>
|
---|
| 89 | #include <iprt/string.h>
|
---|
[5031] | 90 | #include <iprt/assert.h>
|
---|
| 91 | #include <iprt/mem.h>
|
---|
| 92 | #include <iprt/memobj.h>
|
---|
[19454] | 93 | #include <iprt/mp.h>
|
---|
[32572] | 94 | #include <iprt/cpuset.h>
|
---|
| 95 | #include <iprt/spinlock.h>
|
---|
| 96 | #include <iprt/timer.h>
|
---|
[4971] | 97 |
|
---|
[40595] | 98 | #include "dtrace/VBoxVMM.h"
|
---|
[4971] | 99 |
|
---|
[40595] | 100 |
|
---|
[57358] | 101 | /*********************************************************************************************************************************
|
---|
| 102 | * Defined Constants And Macros *
|
---|
| 103 | *********************************************************************************************************************************/
|
---|
[92858] | 104 | #if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_WINDOWS) || defined(DOXYGEN_RUNNING)
|
---|
[32572] | 105 | /** Define this to enable the periodic preemption timer. */
|
---|
| 106 | # define GVMM_SCHED_WITH_PPT
|
---|
| 107 | #endif
|
---|
| 108 |
|
---|
[92892] | 109 | #if /*defined(RT_OS_WINDOWS) ||*/ defined(DOXYGEN_RUNNING)
|
---|
| 110 | /** Define this to enable the per-EMT high resolution wakeup timers. */
|
---|
| 111 | # define GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 112 | #endif
|
---|
[32572] | 113 |
|
---|
[92892] | 114 |
|
---|
[90597] | 115 | /** Special value that GVMMR0DeregisterVCpu sets. */
|
---|
| 116 | #define GVMM_RTNATIVETHREAD_DESTROYED (~(RTNATIVETHREAD)1)
|
---|
| 117 | AssertCompile(GVMM_RTNATIVETHREAD_DESTROYED != NIL_RTNATIVETHREAD);
|
---|
[57249] | 118 |
|
---|
| 119 |
|
---|
[57358] | 120 | /*********************************************************************************************************************************
|
---|
| 121 | * Structures and Typedefs *
|
---|
| 122 | *********************************************************************************************************************************/
|
---|
[4971] | 123 |
|
---|
| 124 | /**
|
---|
| 125 | * Global VM handle.
|
---|
| 126 | */
|
---|
| 127 | typedef struct GVMHANDLE
|
---|
| 128 | {
|
---|
| 129 | /** The index of the next handle in the list (free or used). (0 is nil.) */
|
---|
| 130 | uint16_t volatile iNext;
|
---|
| 131 | /** Our own index / handle value. */
|
---|
| 132 | uint16_t iSelf;
|
---|
[36988] | 133 | /** The process ID of the handle owner.
|
---|
| 134 | * This is used for access checks. */
|
---|
| 135 | RTPROCESS ProcId;
|
---|
[5026] | 136 | /** The pointer to the ring-0 only (aka global) VM structure. */
|
---|
| 137 | PGVM pGVM;
|
---|
[4971] | 138 | /** The virtual machine object. */
|
---|
| 139 | void *pvObj;
|
---|
| 140 | /** The session this VM is associated with. */
|
---|
| 141 | PSUPDRVSESSION pSession;
|
---|
[19398] | 142 | /** The ring-0 handle of the EMT0 thread.
|
---|
| 143 | * This is used for ownership checks as well as looking up a VM handle by thread
|
---|
| 144 | * at times like assertions. */
|
---|
| 145 | RTNATIVETHREAD hEMT0;
|
---|
[4971] | 146 | } GVMHANDLE;
|
---|
| 147 | /** Pointer to a global VM handle. */
|
---|
| 148 | typedef GVMHANDLE *PGVMHANDLE;
|
---|
| 149 |
|
---|
[15865] | 150 | /** Number of GVM handles (including the NIL handle). */
|
---|
| 151 | #if HC_ARCH_BITS == 64
|
---|
[36988] | 152 | # define GVMM_MAX_HANDLES 8192
|
---|
[15865] | 153 | #else
|
---|
| 154 | # define GVMM_MAX_HANDLES 128
|
---|
| 155 | #endif
|
---|
| 156 |
|
---|
[4971] | 157 | /**
|
---|
[32572] | 158 | * Per host CPU GVMM data.
|
---|
| 159 | */
|
---|
| 160 | typedef struct GVMMHOSTCPU
|
---|
| 161 | {
|
---|
| 162 | /** Magic number (GVMMHOSTCPU_MAGIC). */
|
---|
| 163 | uint32_t volatile u32Magic;
|
---|
| 164 | /** The CPU ID. */
|
---|
| 165 | RTCPUID idCpu;
|
---|
| 166 | /** The CPU set index. */
|
---|
| 167 | uint32_t idxCpuSet;
|
---|
| 168 |
|
---|
| 169 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 170 | /** Periodic preemption timer data. */
|
---|
| 171 | struct
|
---|
| 172 | {
|
---|
| 173 | /** The handle to the periodic preemption timer. */
|
---|
| 174 | PRTTIMER pTimer;
|
---|
| 175 | /** Spinlock protecting the data below. */
|
---|
| 176 | RTSPINLOCK hSpinlock;
|
---|
| 177 | /** The smalles Hz that we need to care about. (static) */
|
---|
| 178 | uint32_t uMinHz;
|
---|
| 179 | /** The number of ticks between each historization. */
|
---|
| 180 | uint32_t cTicksHistoriziationInterval;
|
---|
| 181 | /** The current historization tick (counting up to
|
---|
| 182 | * cTicksHistoriziationInterval and then resetting). */
|
---|
| 183 | uint32_t iTickHistorization;
|
---|
| 184 | /** The current timer interval. This is set to 0 when inactive. */
|
---|
| 185 | uint32_t cNsInterval;
|
---|
| 186 | /** The current timer frequency. This is set to 0 when inactive. */
|
---|
| 187 | uint32_t uTimerHz;
|
---|
| 188 | /** The current max frequency reported by the EMTs.
|
---|
| 189 | * This gets historicize and reset by the timer callback. This is
|
---|
| 190 | * read without holding the spinlock, so needs atomic updating. */
|
---|
| 191 | uint32_t volatile uDesiredHz;
|
---|
| 192 | /** Whether the timer was started or not. */
|
---|
| 193 | bool volatile fStarted;
|
---|
| 194 | /** Set if we're starting timer. */
|
---|
| 195 | bool volatile fStarting;
|
---|
| 196 | /** The index of the next history entry (mod it). */
|
---|
| 197 | uint32_t iHzHistory;
|
---|
[33540] | 198 | /** Historicized uDesiredHz values. The array wraps around, new entries
|
---|
[32572] | 199 | * are added at iHzHistory. This is updated approximately every
|
---|
| 200 | * GVMMHOSTCPU_PPT_HIST_INTERVAL_NS by the timer callback. */
|
---|
[32796] | 201 | uint32_t aHzHistory[8];
|
---|
[32572] | 202 | /** Statistics counter for recording the number of interval changes. */
|
---|
[32796] | 203 | uint32_t cChanges;
|
---|
[32572] | 204 | /** Statistics counter for recording the number of timer starts. */
|
---|
[32796] | 205 | uint32_t cStarts;
|
---|
[32572] | 206 | } Ppt;
|
---|
| 207 | #endif /* GVMM_SCHED_WITH_PPT */
|
---|
| 208 |
|
---|
| 209 | } GVMMHOSTCPU;
|
---|
| 210 | /** Pointer to the per host CPU GVMM data. */
|
---|
| 211 | typedef GVMMHOSTCPU *PGVMMHOSTCPU;
|
---|
| 212 | /** The GVMMHOSTCPU::u32Magic value (Petra, Tanya & Rachel Haden). */
|
---|
| 213 | #define GVMMHOSTCPU_MAGIC UINT32_C(0x19711011)
|
---|
| 214 | /** The interval on history entry should cover (approximately) give in
|
---|
| 215 | * nanoseconds. */
|
---|
| 216 | #define GVMMHOSTCPU_PPT_HIST_INTERVAL_NS UINT32_C(20000000)
|
---|
| 217 |
|
---|
| 218 |
|
---|
| 219 | /**
|
---|
[5026] | 220 | * The GVMM instance data.
|
---|
[4971] | 221 | */
|
---|
[5026] | 222 | typedef struct GVMM
|
---|
[4971] | 223 | {
|
---|
| 224 | /** Eyecatcher / magic. */
|
---|
| 225 | uint32_t u32Magic;
|
---|
| 226 | /** The index of the head of the free handle chain. (0 is nil.) */
|
---|
| 227 | uint16_t volatile iFreeHead;
|
---|
| 228 | /** The index of the head of the active handle chain. (0 is nil.) */
|
---|
| 229 | uint16_t volatile iUsedHead;
|
---|
[5167] | 230 | /** The number of VMs. */
|
---|
| 231 | uint16_t volatile cVMs;
|
---|
[33170] | 232 | /** Alignment padding. */
|
---|
| 233 | uint16_t u16Reserved;
|
---|
[19707] | 234 | /** The number of EMTs. */
|
---|
| 235 | uint32_t volatile cEMTs;
|
---|
[33170] | 236 | /** The number of EMTs that have halted in GVMMR0SchedHalt. */
|
---|
| 237 | uint32_t volatile cHaltedEMTs;
|
---|
[58830] | 238 | /** Mini lock for restricting early wake-ups to one thread. */
|
---|
| 239 | bool volatile fDoingEarlyWakeUps;
|
---|
| 240 | bool afPadding[3]; /**< explicit alignment padding. */
|
---|
[33170] | 241 | /** When the next halted or sleeping EMT will wake up.
|
---|
| 242 | * This is set to 0 when it needs recalculating and to UINT64_MAX when
|
---|
| 243 | * there are no halted or sleeping EMTs in the GVMM. */
|
---|
| 244 | uint64_t uNsNextEmtWakeup;
|
---|
[5167] | 245 | /** The lock used to serialize VM creation, destruction and associated events that
|
---|
| 246 | * isn't performance critical. Owners may acquire the list lock. */
|
---|
[58830] | 247 | RTCRITSECT CreateDestroyLock;
|
---|
[5167] | 248 | /** The lock used to serialize used list updates and accesses.
|
---|
| 249 | * This indirectly includes scheduling since the scheduler will have to walk the
|
---|
| 250 | * used list to examin running VMs. Owners may not acquire any other locks. */
|
---|
[58830] | 251 | RTCRITSECTRW UsedLock;
|
---|
[4971] | 252 | /** The handle array.
|
---|
| 253 | * The size of this array defines the maximum number of currently running VMs.
|
---|
| 254 | * The first entry is unused as it represents the NIL handle. */
|
---|
[15865] | 255 | GVMHANDLE aHandles[GVMM_MAX_HANDLES];
|
---|
[5240] | 256 |
|
---|
[19707] | 257 | /** @gcfgm{/GVMM/cEMTsMeansCompany, 32-bit, 0, UINT32_MAX, 1}
|
---|
| 258 | * The number of EMTs that means we no longer consider ourselves alone on a
|
---|
| 259 | * CPU/Core.
|
---|
[5240] | 260 | */
|
---|
[19707] | 261 | uint32_t cEMTsMeansCompany;
|
---|
[6289] | 262 | /** @gcfgm{/GVMM/MinSleepAlone,32-bit, 0, 100000000, 750000, ns}
|
---|
| 263 | * The minimum sleep time for when we're alone, in nano seconds.
|
---|
[5244] | 264 | */
|
---|
| 265 | uint32_t nsMinSleepAlone;
|
---|
[6289] | 266 | /** @gcfgm{/GVMM/MinSleepCompany,32-bit,0, 100000000, 15000, ns}
|
---|
| 267 | * The minimum sleep time for when we've got company, in nano seconds.
|
---|
[5244] | 268 | */
|
---|
| 269 | uint32_t nsMinSleepCompany;
|
---|
[92892] | 270 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 271 | /** @gcfgm{/GVMM/MinSleepWithHrWakeUp,32-bit,0, 100000000, 5000, ns}
|
---|
| 272 | * The minimum sleep time for when we've got a high-resolution wake-up timer, in
|
---|
| 273 | * nano seconds.
|
---|
| 274 | */
|
---|
| 275 | uint32_t nsMinSleepWithHrTimer;
|
---|
| 276 | #endif
|
---|
[6289] | 277 | /** @gcfgm{/GVMM/EarlyWakeUp1, 32-bit, 0, 100000000, 25000, ns}
|
---|
[58830] | 278 | * The limit for the first round of early wake-ups, given in nano seconds.
|
---|
[5240] | 279 | */
|
---|
| 280 | uint32_t nsEarlyWakeUp1;
|
---|
[6289] | 281 | /** @gcfgm{/GVMM/EarlyWakeUp2, 32-bit, 0, 100000000, 50000, ns}
|
---|
[58830] | 282 | * The limit for the second round of early wake-ups, given in nano seconds.
|
---|
[5240] | 283 | */
|
---|
| 284 | uint32_t nsEarlyWakeUp2;
|
---|
[32572] | 285 |
|
---|
[58830] | 286 | /** Set if we're doing early wake-ups.
|
---|
| 287 | * This reflects nsEarlyWakeUp1 and nsEarlyWakeUp2. */
|
---|
| 288 | bool volatile fDoEarlyWakeUps;
|
---|
| 289 |
|
---|
[32572] | 290 | /** The number of entries in the host CPU array (aHostCpus). */
|
---|
| 291 | uint32_t cHostCpus;
|
---|
| 292 | /** Per host CPU data (variable length). */
|
---|
| 293 | GVMMHOSTCPU aHostCpus[1];
|
---|
[5026] | 294 | } GVMM;
|
---|
[58830] | 295 | AssertCompileMemberAlignment(GVMM, CreateDestroyLock, 8);
|
---|
| 296 | AssertCompileMemberAlignment(GVMM, UsedLock, 8);
|
---|
| 297 | AssertCompileMemberAlignment(GVMM, uNsNextEmtWakeup, 8);
|
---|
[5026] | 298 | /** Pointer to the GVMM instance data. */
|
---|
| 299 | typedef GVMM *PGVMM;
|
---|
[4971] | 300 |
|
---|
[5026] | 301 | /** The GVMM::u32Magic value (Charlie Haden). */
|
---|
[39038] | 302 | #define GVMM_MAGIC UINT32_C(0x19370806)
|
---|
[4971] | 303 |
|
---|
| 304 |
|
---|
| 305 |
|
---|
[57358] | 306 | /*********************************************************************************************************************************
|
---|
| 307 | * Global Variables *
|
---|
| 308 | *********************************************************************************************************************************/
|
---|
[5026] | 309 | /** Pointer to the GVMM instance data.
|
---|
[4971] | 310 | * (Just my general dislike for global variables.) */
|
---|
[5026] | 311 | static PGVMM g_pGVMM = NULL;
|
---|
[4971] | 312 |
|
---|
[5031] | 313 | /** Macro for obtaining and validating the g_pGVMM pointer.
|
---|
| 314 | * On failure it will return from the invoking function with the specified return value.
|
---|
| 315 | *
|
---|
| 316 | * @param pGVMM The name of the pGVMM variable.
|
---|
[39402] | 317 | * @param rc The return value on failure. Use VERR_GVMM_INSTANCE for VBox
|
---|
| 318 | * status codes.
|
---|
[5031] | 319 | */
|
---|
| 320 | #define GVMM_GET_VALID_INSTANCE(pGVMM, rc) \
|
---|
| 321 | do { \
|
---|
| 322 | (pGVMM) = g_pGVMM;\
|
---|
| 323 | AssertPtrReturn((pGVMM), (rc)); \
|
---|
| 324 | AssertMsgReturn((pGVMM)->u32Magic == GVMM_MAGIC, ("%p - %#x\n", (pGVMM), (pGVMM)->u32Magic), (rc)); \
|
---|
| 325 | } while (0)
|
---|
[4971] | 326 |
|
---|
[5031] | 327 | /** Macro for obtaining and validating the g_pGVMM pointer, void function variant.
|
---|
| 328 | * On failure it will return from the invoking function.
|
---|
| 329 | *
|
---|
| 330 | * @param pGVMM The name of the pGVMM variable.
|
---|
| 331 | */
|
---|
| 332 | #define GVMM_GET_VALID_INSTANCE_VOID(pGVMM) \
|
---|
| 333 | do { \
|
---|
| 334 | (pGVMM) = g_pGVMM;\
|
---|
| 335 | AssertPtrReturnVoid((pGVMM)); \
|
---|
| 336 | AssertMsgReturnVoid((pGVMM)->u32Magic == GVMM_MAGIC, ("%p - %#x\n", (pGVMM), (pGVMM)->u32Magic)); \
|
---|
| 337 | } while (0)
|
---|
| 338 |
|
---|
| 339 |
|
---|
[57358] | 340 | /*********************************************************************************************************************************
|
---|
| 341 | * Internal Functions *
|
---|
| 342 | *********************************************************************************************************************************/
|
---|
[78431] | 343 | static void gvmmR0InitPerVMData(PGVM pGVM, int16_t hSelf, VMCPUID cCpus, PSUPDRVSESSION pSession);
|
---|
[5026] | 344 | static DECLCALLBACK(void) gvmmR0HandleObjDestructor(void *pvObj, void *pvGVMM, void *pvHandle);
|
---|
[80346] | 345 | static int gvmmR0ByGVM(PGVM pGVM, PGVMM *ppGVMM, bool fTakeUsedLock);
|
---|
| 346 | static int gvmmR0ByGVMandEMT(PGVM pGVM, VMCPUID idCpu, PGVMM *ppGVMM);
|
---|
[67955] | 347 |
|
---|
[32572] | 348 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 349 | static DECLCALLBACK(void) gvmmR0SchedPeriodicPreemptionTimerCallback(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
|
---|
| 350 | #endif
|
---|
[92892] | 351 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 352 | static DECLCALLBACK(void) gvmmR0EmtWakeUpTimerCallback(PRTTIMER pTimer, void *pvUser, uint64_t iTick);
|
---|
| 353 | #endif
|
---|
[4971] | 354 |
|
---|
| 355 |
|
---|
| 356 | /**
|
---|
[5026] | 357 | * Initializes the GVMM.
|
---|
[4971] | 358 | *
|
---|
[33540] | 359 | * This is called while owning the loader semaphore (see supdrvIOCtl_LdrLoad()).
|
---|
[4971] | 360 | *
|
---|
| 361 | * @returns VBox status code.
|
---|
| 362 | */
|
---|
[5026] | 363 | GVMMR0DECL(int) GVMMR0Init(void)
|
---|
[4971] | 364 | {
|
---|
[5211] | 365 | LogFlow(("GVMMR0Init:\n"));
|
---|
[4971] | 366 |
|
---|
| 367 | /*
|
---|
| 368 | * Allocate and initialize the instance data.
|
---|
| 369 | */
|
---|
[32572] | 370 | uint32_t cHostCpus = RTMpGetArraySize();
|
---|
[39402] | 371 | AssertMsgReturn(cHostCpus > 0 && cHostCpus < _64K, ("%d", (int)cHostCpus), VERR_GVMM_HOST_CPU_RANGE);
|
---|
[32572] | 372 |
|
---|
[73097] | 373 | PGVMM pGVMM = (PGVMM)RTMemAllocZ(RT_UOFFSETOF_DYN(GVMM, aHostCpus[cHostCpus]));
|
---|
[5026] | 374 | if (!pGVMM)
|
---|
[4971] | 375 | return VERR_NO_MEMORY;
|
---|
[58830] | 376 | int rc = RTCritSectInitEx(&pGVMM->CreateDestroyLock, 0, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE,
|
---|
| 377 | "GVMM-CreateDestroyLock");
|
---|
[4971] | 378 | if (RT_SUCCESS(rc))
|
---|
| 379 | {
|
---|
[58830] | 380 | rc = RTCritSectRwInitEx(&pGVMM->UsedLock, 0, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, "GVMM-UsedLock");
|
---|
[5167] | 381 | if (RT_SUCCESS(rc))
|
---|
| 382 | {
|
---|
| 383 | pGVMM->u32Magic = GVMM_MAGIC;
|
---|
| 384 | pGVMM->iUsedHead = 0;
|
---|
| 385 | pGVMM->iFreeHead = 1;
|
---|
[4971] | 386 |
|
---|
[5167] | 387 | /* the nil handle */
|
---|
| 388 | pGVMM->aHandles[0].iSelf = 0;
|
---|
| 389 | pGVMM->aHandles[0].iNext = 0;
|
---|
[4971] | 390 |
|
---|
[5167] | 391 | /* the tail */
|
---|
[5941] | 392 | unsigned i = RT_ELEMENTS(pGVMM->aHandles) - 1;
|
---|
[5167] | 393 | pGVMM->aHandles[i].iSelf = i;
|
---|
| 394 | pGVMM->aHandles[i].iNext = 0; /* nil */
|
---|
[4971] | 395 |
|
---|
[5167] | 396 | /* the rest */
|
---|
| 397 | while (i-- > 1)
|
---|
| 398 | {
|
---|
| 399 | pGVMM->aHandles[i].iSelf = i;
|
---|
| 400 | pGVMM->aHandles[i].iNext = i + 1;
|
---|
| 401 | }
|
---|
| 402 |
|
---|
[5240] | 403 | /* The default configuration values. */
|
---|
[33170] | 404 | uint32_t cNsResolution = RTSemEventMultiGetResolution();
|
---|
| 405 | pGVMM->cEMTsMeansCompany = 1; /** @todo should be adjusted to relative to the cpu count or something... */
|
---|
| 406 | if (cNsResolution >= 5*RT_NS_100US)
|
---|
| 407 | {
|
---|
| 408 | pGVMM->nsMinSleepAlone = 750000 /* ns (0.750 ms) */; /** @todo this should be adjusted to be 75% (or something) of the scheduler granularity... */
|
---|
| 409 | pGVMM->nsMinSleepCompany = 15000 /* ns (0.015 ms) */;
|
---|
| 410 | pGVMM->nsEarlyWakeUp1 = 25000 /* ns (0.025 ms) */;
|
---|
| 411 | pGVMM->nsEarlyWakeUp2 = 50000 /* ns (0.050 ms) */;
|
---|
| 412 | }
|
---|
[33172] | 413 | else if (cNsResolution > RT_NS_100US)
|
---|
| 414 | {
|
---|
| 415 | pGVMM->nsMinSleepAlone = cNsResolution / 2;
|
---|
| 416 | pGVMM->nsMinSleepCompany = cNsResolution / 4;
|
---|
| 417 | pGVMM->nsEarlyWakeUp1 = 0;
|
---|
| 418 | pGVMM->nsEarlyWakeUp2 = 0;
|
---|
| 419 | }
|
---|
[33170] | 420 | else
|
---|
| 421 | {
|
---|
[33172] | 422 | pGVMM->nsMinSleepAlone = 2000;
|
---|
| 423 | pGVMM->nsMinSleepCompany = 2000;
|
---|
[33170] | 424 | pGVMM->nsEarlyWakeUp1 = 0;
|
---|
| 425 | pGVMM->nsEarlyWakeUp2 = 0;
|
---|
| 426 | }
|
---|
[92892] | 427 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 428 | pGVMM->nsMinSleepWithHrTimer = 5000 /* ns (0.005 ms) */;
|
---|
| 429 | #endif
|
---|
[58830] | 430 | pGVMM->fDoEarlyWakeUps = pGVMM->nsEarlyWakeUp1 > 0 && pGVMM->nsEarlyWakeUp2 > 0;
|
---|
[5240] | 431 |
|
---|
[32572] | 432 | /* The host CPU data. */
|
---|
| 433 | pGVMM->cHostCpus = cHostCpus;
|
---|
| 434 | uint32_t iCpu = cHostCpus;
|
---|
| 435 | RTCPUSET PossibleSet;
|
---|
| 436 | RTMpGetSet(&PossibleSet);
|
---|
| 437 | while (iCpu-- > 0)
|
---|
| 438 | {
|
---|
| 439 | pGVMM->aHostCpus[iCpu].idxCpuSet = iCpu;
|
---|
| 440 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 441 | pGVMM->aHostCpus[iCpu].Ppt.pTimer = NULL;
|
---|
| 442 | pGVMM->aHostCpus[iCpu].Ppt.hSpinlock = NIL_RTSPINLOCK;
|
---|
| 443 | pGVMM->aHostCpus[iCpu].Ppt.uMinHz = 5; /** @todo Add some API which figures this one out. (not *that* important) */
|
---|
| 444 | pGVMM->aHostCpus[iCpu].Ppt.cTicksHistoriziationInterval = 1;
|
---|
| 445 | //pGVMM->aHostCpus[iCpu].Ppt.iTickHistorization = 0;
|
---|
| 446 | //pGVMM->aHostCpus[iCpu].Ppt.cNsInterval = 0;
|
---|
| 447 | //pGVMM->aHostCpus[iCpu].Ppt.uTimerHz = 0;
|
---|
| 448 | //pGVMM->aHostCpus[iCpu].Ppt.uDesiredHz = 0;
|
---|
| 449 | //pGVMM->aHostCpus[iCpu].Ppt.fStarted = false;
|
---|
| 450 | //pGVMM->aHostCpus[iCpu].Ppt.fStarting = false;
|
---|
| 451 | //pGVMM->aHostCpus[iCpu].Ppt.iHzHistory = 0;
|
---|
| 452 | //pGVMM->aHostCpus[iCpu].Ppt.aHzHistory = {0};
|
---|
| 453 | #endif
|
---|
| 454 |
|
---|
| 455 | if (RTCpuSetIsMember(&PossibleSet, iCpu))
|
---|
| 456 | {
|
---|
| 457 | pGVMM->aHostCpus[iCpu].idCpu = RTMpCpuIdFromSetIndex(iCpu);
|
---|
| 458 | pGVMM->aHostCpus[iCpu].u32Magic = GVMMHOSTCPU_MAGIC;
|
---|
| 459 |
|
---|
| 460 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 461 | rc = RTTimerCreateEx(&pGVMM->aHostCpus[iCpu].Ppt.pTimer,
|
---|
| 462 | 50*1000*1000 /* whatever */,
|
---|
| 463 | RTTIMER_FLAGS_CPU(iCpu) | RTTIMER_FLAGS_HIGH_RES,
|
---|
| 464 | gvmmR0SchedPeriodicPreemptionTimerCallback,
|
---|
| 465 | &pGVMM->aHostCpus[iCpu]);
|
---|
| 466 | if (RT_SUCCESS(rc))
|
---|
[92870] | 467 | {
|
---|
[40806] | 468 | rc = RTSpinlockCreate(&pGVMM->aHostCpus[iCpu].Ppt.hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "GVMM/CPU");
|
---|
[92870] | 469 | if (RT_FAILURE(rc))
|
---|
| 470 | LogRel(("GVMMR0Init: RTSpinlockCreate failed for #%u (%d)\n", iCpu, rc));
|
---|
| 471 | }
|
---|
| 472 | else
|
---|
| 473 | LogRel(("GVMMR0Init: RTTimerCreateEx failed for #%u (%d)\n", iCpu, rc));
|
---|
[32572] | 474 | if (RT_FAILURE(rc))
|
---|
| 475 | {
|
---|
| 476 | while (iCpu < cHostCpus)
|
---|
| 477 | {
|
---|
| 478 | RTTimerDestroy(pGVMM->aHostCpus[iCpu].Ppt.pTimer);
|
---|
| 479 | RTSpinlockDestroy(pGVMM->aHostCpus[iCpu].Ppt.hSpinlock);
|
---|
| 480 | pGVMM->aHostCpus[iCpu].Ppt.hSpinlock = NIL_RTSPINLOCK;
|
---|
| 481 | iCpu++;
|
---|
| 482 | }
|
---|
| 483 | break;
|
---|
| 484 | }
|
---|
| 485 | #endif
|
---|
| 486 | }
|
---|
| 487 | else
|
---|
| 488 | {
|
---|
| 489 | pGVMM->aHostCpus[iCpu].idCpu = NIL_RTCPUID;
|
---|
| 490 | pGVMM->aHostCpus[iCpu].u32Magic = 0;
|
---|
| 491 | }
|
---|
| 492 | }
|
---|
| 493 | if (RT_SUCCESS(rc))
|
---|
| 494 | {
|
---|
| 495 | g_pGVMM = pGVMM;
|
---|
| 496 | LogFlow(("GVMMR0Init: pGVMM=%p cHostCpus=%u\n", pGVMM, cHostCpus));
|
---|
| 497 | return VINF_SUCCESS;
|
---|
| 498 | }
|
---|
| 499 |
|
---|
| 500 | /* bail out. */
|
---|
[58830] | 501 | RTCritSectRwDelete(&pGVMM->UsedLock);
|
---|
[4971] | 502 | }
|
---|
[92870] | 503 | else
|
---|
| 504 | LogRel(("GVMMR0Init: RTCritSectRwInitEx failed (%d)\n", rc));
|
---|
[58830] | 505 | RTCritSectDelete(&pGVMM->CreateDestroyLock);
|
---|
[4971] | 506 | }
|
---|
[92870] | 507 | else
|
---|
| 508 | LogRel(("GVMMR0Init: RTCritSectInitEx failed (%d)\n", rc));
|
---|
[4971] | 509 |
|
---|
[5026] | 510 | RTMemFree(pGVMM);
|
---|
[4971] | 511 | return rc;
|
---|
| 512 | }
|
---|
| 513 |
|
---|
| 514 |
|
---|
| 515 | /**
|
---|
| 516 | * Terminates the GVM.
|
---|
| 517 | *
|
---|
| 518 | * This is called while owning the loader semaphore (see supdrvLdrFree()).
|
---|
| 519 | * And unless something is wrong, there should be absolutely no VMs
|
---|
| 520 | * registered at this point.
|
---|
| 521 | */
|
---|
[5026] | 522 | GVMMR0DECL(void) GVMMR0Term(void)
|
---|
[4971] | 523 | {
|
---|
[5211] | 524 | LogFlow(("GVMMR0Term:\n"));
|
---|
[4971] | 525 |
|
---|
[5026] | 526 | PGVMM pGVMM = g_pGVMM;
|
---|
| 527 | g_pGVMM = NULL;
|
---|
[80346] | 528 | if (RT_UNLIKELY(!RT_VALID_PTR(pGVMM)))
|
---|
[4971] | 529 | {
|
---|
[66439] | 530 | SUPR0Printf("GVMMR0Term: pGVMM=%RKv\n", pGVMM);
|
---|
[4971] | 531 | return;
|
---|
| 532 | }
|
---|
| 533 |
|
---|
[32572] | 534 | /*
|
---|
| 535 | * First of all, stop all active timers.
|
---|
| 536 | */
|
---|
| 537 | uint32_t cActiveTimers = 0;
|
---|
| 538 | uint32_t iCpu = pGVMM->cHostCpus;
|
---|
| 539 | while (iCpu-- > 0)
|
---|
| 540 | {
|
---|
| 541 | ASMAtomicWriteU32(&pGVMM->aHostCpus[iCpu].u32Magic, ~GVMMHOSTCPU_MAGIC);
|
---|
| 542 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 543 | if ( pGVMM->aHostCpus[iCpu].Ppt.pTimer != NULL
|
---|
| 544 | && RT_SUCCESS(RTTimerStop(pGVMM->aHostCpus[iCpu].Ppt.pTimer)))
|
---|
| 545 | cActiveTimers++;
|
---|
| 546 | #endif
|
---|
| 547 | }
|
---|
| 548 | if (cActiveTimers)
|
---|
| 549 | RTThreadSleep(1); /* fudge */
|
---|
[5167] | 550 |
|
---|
[32572] | 551 | /*
|
---|
| 552 | * Invalidate the and free resources.
|
---|
| 553 | */
|
---|
| 554 | pGVMM->u32Magic = ~GVMM_MAGIC;
|
---|
[58830] | 555 | RTCritSectRwDelete(&pGVMM->UsedLock);
|
---|
| 556 | RTCritSectDelete(&pGVMM->CreateDestroyLock);
|
---|
[5167] | 557 |
|
---|
[5026] | 558 | pGVMM->iFreeHead = 0;
|
---|
| 559 | if (pGVMM->iUsedHead)
|
---|
[4971] | 560 | {
|
---|
[19707] | 561 | SUPR0Printf("GVMMR0Term: iUsedHead=%#x! (cVMs=%#x cEMTs=%#x)\n", pGVMM->iUsedHead, pGVMM->cVMs, pGVMM->cEMTs);
|
---|
[5026] | 562 | pGVMM->iUsedHead = 0;
|
---|
[4971] | 563 | }
|
---|
| 564 |
|
---|
[32572] | 565 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 566 | iCpu = pGVMM->cHostCpus;
|
---|
| 567 | while (iCpu-- > 0)
|
---|
| 568 | {
|
---|
| 569 | RTTimerDestroy(pGVMM->aHostCpus[iCpu].Ppt.pTimer);
|
---|
| 570 | pGVMM->aHostCpus[iCpu].Ppt.pTimer = NULL;
|
---|
| 571 | RTSpinlockDestroy(pGVMM->aHostCpus[iCpu].Ppt.hSpinlock);
|
---|
| 572 | pGVMM->aHostCpus[iCpu].Ppt.hSpinlock = NIL_RTSPINLOCK;
|
---|
| 573 | }
|
---|
| 574 | #endif
|
---|
| 575 |
|
---|
[5026] | 576 | RTMemFree(pGVMM);
|
---|
[4971] | 577 | }
|
---|
| 578 |
|
---|
| 579 |
|
---|
| 580 | /**
|
---|
[5240] | 581 | * A quick hack for setting global config values.
|
---|
| 582 | *
|
---|
| 583 | * @returns VBox status code.
|
---|
| 584 | *
|
---|
| 585 | * @param pSession The session handle. Used for authentication.
|
---|
| 586 | * @param pszName The variable name.
|
---|
| 587 | * @param u64Value The new value.
|
---|
| 588 | */
|
---|
| 589 | GVMMR0DECL(int) GVMMR0SetConfig(PSUPDRVSESSION pSession, const char *pszName, uint64_t u64Value)
|
---|
| 590 | {
|
---|
| 591 | /*
|
---|
| 592 | * Validate input.
|
---|
| 593 | */
|
---|
| 594 | PGVMM pGVMM;
|
---|
[39402] | 595 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
[5240] | 596 | AssertPtrReturn(pSession, VERR_INVALID_HANDLE);
|
---|
| 597 | AssertPtrReturn(pszName, VERR_INVALID_POINTER);
|
---|
| 598 |
|
---|
| 599 | /*
|
---|
| 600 | * String switch time!
|
---|
| 601 | */
|
---|
[46326] | 602 | if (strncmp(pszName, RT_STR_TUPLE("/GVMM/")))
|
---|
[5240] | 603 | return VERR_CFGM_VALUE_NOT_FOUND; /* borrow status codes from CFGM... */
|
---|
| 604 | int rc = VINF_SUCCESS;
|
---|
| 605 | pszName += sizeof("/GVMM/") - 1;
|
---|
[19707] | 606 | if (!strcmp(pszName, "cEMTsMeansCompany"))
|
---|
[5240] | 607 | {
|
---|
[5244] | 608 | if (u64Value <= UINT32_MAX)
|
---|
[19707] | 609 | pGVMM->cEMTsMeansCompany = u64Value;
|
---|
[5244] | 610 | else
|
---|
| 611 | rc = VERR_OUT_OF_RANGE;
|
---|
| 612 | }
|
---|
| 613 | else if (!strcmp(pszName, "MinSleepAlone"))
|
---|
| 614 | {
|
---|
[33170] | 615 | if (u64Value <= RT_NS_100MS)
|
---|
[5244] | 616 | pGVMM->nsMinSleepAlone = u64Value;
|
---|
[5240] | 617 | else
|
---|
| 618 | rc = VERR_OUT_OF_RANGE;
|
---|
| 619 | }
|
---|
[5244] | 620 | else if (!strcmp(pszName, "MinSleepCompany"))
|
---|
| 621 | {
|
---|
[33170] | 622 | if (u64Value <= RT_NS_100MS)
|
---|
[5244] | 623 | pGVMM->nsMinSleepCompany = u64Value;
|
---|
| 624 | else
|
---|
| 625 | rc = VERR_OUT_OF_RANGE;
|
---|
| 626 | }
|
---|
[92892] | 627 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 628 | else if (!strcmp(pszName, "MinSleepWithHrWakeUp"))
|
---|
| 629 | {
|
---|
| 630 | if (u64Value <= RT_NS_100MS)
|
---|
| 631 | pGVMM->nsMinSleepWithHrTimer = u64Value;
|
---|
| 632 | else
|
---|
| 633 | rc = VERR_OUT_OF_RANGE;
|
---|
| 634 | }
|
---|
| 635 | #endif
|
---|
[5240] | 636 | else if (!strcmp(pszName, "EarlyWakeUp1"))
|
---|
| 637 | {
|
---|
[33170] | 638 | if (u64Value <= RT_NS_100MS)
|
---|
[58830] | 639 | {
|
---|
[5240] | 640 | pGVMM->nsEarlyWakeUp1 = u64Value;
|
---|
[58830] | 641 | pGVMM->fDoEarlyWakeUps = pGVMM->nsEarlyWakeUp1 > 0 && pGVMM->nsEarlyWakeUp2 > 0;
|
---|
| 642 | }
|
---|
[5240] | 643 | else
|
---|
| 644 | rc = VERR_OUT_OF_RANGE;
|
---|
| 645 | }
|
---|
| 646 | else if (!strcmp(pszName, "EarlyWakeUp2"))
|
---|
| 647 | {
|
---|
[33170] | 648 | if (u64Value <= RT_NS_100MS)
|
---|
[58830] | 649 | {
|
---|
[5240] | 650 | pGVMM->nsEarlyWakeUp2 = u64Value;
|
---|
[58830] | 651 | pGVMM->fDoEarlyWakeUps = pGVMM->nsEarlyWakeUp1 > 0 && pGVMM->nsEarlyWakeUp2 > 0;
|
---|
| 652 | }
|
---|
[5240] | 653 | else
|
---|
| 654 | rc = VERR_OUT_OF_RANGE;
|
---|
| 655 | }
|
---|
| 656 | else
|
---|
| 657 | rc = VERR_CFGM_VALUE_NOT_FOUND;
|
---|
| 658 | return rc;
|
---|
| 659 | }
|
---|
| 660 |
|
---|
| 661 |
|
---|
| 662 | /**
|
---|
| 663 | * A quick hack for getting global config values.
|
---|
| 664 | *
|
---|
| 665 | * @returns VBox status code.
|
---|
| 666 | *
|
---|
| 667 | * @param pSession The session handle. Used for authentication.
|
---|
| 668 | * @param pszName The variable name.
|
---|
[58126] | 669 | * @param pu64Value Where to return the value.
|
---|
[5240] | 670 | */
|
---|
| 671 | GVMMR0DECL(int) GVMMR0QueryConfig(PSUPDRVSESSION pSession, const char *pszName, uint64_t *pu64Value)
|
---|
| 672 | {
|
---|
| 673 | /*
|
---|
| 674 | * Validate input.
|
---|
| 675 | */
|
---|
| 676 | PGVMM pGVMM;
|
---|
[39402] | 677 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
[5240] | 678 | AssertPtrReturn(pSession, VERR_INVALID_HANDLE);
|
---|
| 679 | AssertPtrReturn(pszName, VERR_INVALID_POINTER);
|
---|
| 680 | AssertPtrReturn(pu64Value, VERR_INVALID_POINTER);
|
---|
| 681 |
|
---|
| 682 | /*
|
---|
| 683 | * String switch time!
|
---|
| 684 | */
|
---|
[46326] | 685 | if (strncmp(pszName, RT_STR_TUPLE("/GVMM/")))
|
---|
[5240] | 686 | return VERR_CFGM_VALUE_NOT_FOUND; /* borrow status codes from CFGM... */
|
---|
| 687 | int rc = VINF_SUCCESS;
|
---|
| 688 | pszName += sizeof("/GVMM/") - 1;
|
---|
[19707] | 689 | if (!strcmp(pszName, "cEMTsMeansCompany"))
|
---|
| 690 | *pu64Value = pGVMM->cEMTsMeansCompany;
|
---|
[5244] | 691 | else if (!strcmp(pszName, "MinSleepAlone"))
|
---|
| 692 | *pu64Value = pGVMM->nsMinSleepAlone;
|
---|
| 693 | else if (!strcmp(pszName, "MinSleepCompany"))
|
---|
| 694 | *pu64Value = pGVMM->nsMinSleepCompany;
|
---|
[92892] | 695 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 696 | else if (!strcmp(pszName, "MinSleepWithHrWakeUp"))
|
---|
| 697 | *pu64Value = pGVMM->nsMinSleepWithHrTimer;
|
---|
| 698 | #endif
|
---|
[5240] | 699 | else if (!strcmp(pszName, "EarlyWakeUp1"))
|
---|
| 700 | *pu64Value = pGVMM->nsEarlyWakeUp1;
|
---|
| 701 | else if (!strcmp(pszName, "EarlyWakeUp2"))
|
---|
| 702 | *pu64Value = pGVMM->nsEarlyWakeUp2;
|
---|
| 703 | else
|
---|
| 704 | rc = VERR_CFGM_VALUE_NOT_FOUND;
|
---|
| 705 | return rc;
|
---|
| 706 | }
|
---|
| 707 |
|
---|
| 708 |
|
---|
| 709 | /**
|
---|
[58830] | 710 | * Acquire the 'used' lock in shared mode.
|
---|
[6635] | 711 | *
|
---|
[58830] | 712 | * This prevents destruction of the VM while we're in ring-0.
|
---|
| 713 | *
|
---|
[6635] | 714 | * @returns IPRT status code, see RTSemFastMutexRequest.
|
---|
[58830] | 715 | * @param a_pGVMM The GVMM instance data.
|
---|
| 716 | * @sa GVMMR0_USED_SHARED_UNLOCK, GVMMR0_USED_EXCLUSIVE_LOCK
|
---|
[6635] | 717 | */
|
---|
[58830] | 718 | #define GVMMR0_USED_SHARED_LOCK(a_pGVMM) RTCritSectRwEnterShared(&(a_pGVMM)->UsedLock)
|
---|
[6635] | 719 |
|
---|
[58830] | 720 | /**
|
---|
| 721 | * Release the 'used' lock in when owning it in shared mode.
|
---|
| 722 | *
|
---|
| 723 | * @returns IPRT status code, see RTSemFastMutexRequest.
|
---|
| 724 | * @param a_pGVMM The GVMM instance data.
|
---|
| 725 | * @sa GVMMR0_USED_SHARED_LOCK
|
---|
| 726 | */
|
---|
| 727 | #define GVMMR0_USED_SHARED_UNLOCK(a_pGVMM) RTCritSectRwLeaveShared(&(a_pGVMM)->UsedLock)
|
---|
[6635] | 728 |
|
---|
| 729 | /**
|
---|
[58830] | 730 | * Acquire the 'used' lock in exclusive mode.
|
---|
[6635] | 731 | *
|
---|
[58830] | 732 | * Only use this function when making changes to the used list.
|
---|
| 733 | *
|
---|
| 734 | * @returns IPRT status code, see RTSemFastMutexRequest.
|
---|
| 735 | * @param a_pGVMM The GVMM instance data.
|
---|
| 736 | * @sa GVMMR0_USED_EXCLUSIVE_UNLOCK
|
---|
| 737 | */
|
---|
| 738 | #define GVMMR0_USED_EXCLUSIVE_LOCK(a_pGVMM) RTCritSectRwEnterExcl(&(a_pGVMM)->UsedLock)
|
---|
| 739 |
|
---|
| 740 | /**
|
---|
| 741 | * Release the 'used' lock when owning it in exclusive mode.
|
---|
| 742 | *
|
---|
[6635] | 743 | * @returns IPRT status code, see RTSemFastMutexRelease.
|
---|
[58833] | 744 | * @param a_pGVMM The GVMM instance data.
|
---|
[58830] | 745 | * @sa GVMMR0_USED_EXCLUSIVE_LOCK, GVMMR0_USED_SHARED_UNLOCK
|
---|
[6635] | 746 | */
|
---|
[58830] | 747 | #define GVMMR0_USED_EXCLUSIVE_UNLOCK(a_pGVMM) RTCritSectRwLeaveExcl(&(a_pGVMM)->UsedLock)
|
---|
[6635] | 748 |
|
---|
| 749 |
|
---|
| 750 | /**
|
---|
| 751 | * Try acquire the 'create & destroy' lock.
|
---|
| 752 | *
|
---|
| 753 | * @returns IPRT status code, see RTSemFastMutexRequest.
|
---|
| 754 | * @param pGVMM The GVMM instance data.
|
---|
| 755 | */
|
---|
| 756 | DECLINLINE(int) gvmmR0CreateDestroyLock(PGVMM pGVMM)
|
---|
| 757 | {
|
---|
| 758 | LogFlow(("++gvmmR0CreateDestroyLock(%p)\n", pGVMM));
|
---|
[58830] | 759 | int rc = RTCritSectEnter(&pGVMM->CreateDestroyLock);
|
---|
[6635] | 760 | LogFlow(("gvmmR0CreateDestroyLock(%p)->%Rrc\n", pGVMM, rc));
|
---|
| 761 | return rc;
|
---|
| 762 | }
|
---|
| 763 |
|
---|
| 764 |
|
---|
| 765 | /**
|
---|
| 766 | * Release the 'create & destroy' lock.
|
---|
| 767 | *
|
---|
| 768 | * @returns IPRT status code, see RTSemFastMutexRequest.
|
---|
| 769 | * @param pGVMM The GVMM instance data.
|
---|
| 770 | */
|
---|
| 771 | DECLINLINE(int) gvmmR0CreateDestroyUnlock(PGVMM pGVMM)
|
---|
| 772 | {
|
---|
| 773 | LogFlow(("--gvmmR0CreateDestroyUnlock(%p)\n", pGVMM));
|
---|
[58830] | 774 | int rc = RTCritSectLeave(&pGVMM->CreateDestroyLock);
|
---|
[6635] | 775 | AssertRC(rc);
|
---|
| 776 | return rc;
|
---|
| 777 | }
|
---|
| 778 |
|
---|
| 779 |
|
---|
| 780 | /**
|
---|
[5031] | 781 | * Request wrapper for the GVMMR0CreateVM API.
|
---|
| 782 | *
|
---|
| 783 | * @returns VBox status code.
|
---|
[5211] | 784 | * @param pReq The request buffer.
|
---|
[67955] | 785 | * @param pSession The session handle. The VM will be associated with this.
|
---|
[5031] | 786 | */
|
---|
[67955] | 787 | GVMMR0DECL(int) GVMMR0CreateVMReq(PGVMMCREATEVMREQ pReq, PSUPDRVSESSION pSession)
|
---|
[5031] | 788 | {
|
---|
| 789 | /*
|
---|
| 790 | * Validate the request.
|
---|
| 791 | */
|
---|
[80346] | 792 | if (!RT_VALID_PTR(pReq))
|
---|
[5031] | 793 | return VERR_INVALID_POINTER;
|
---|
| 794 | if (pReq->Hdr.cbReq != sizeof(*pReq))
|
---|
| 795 | return VERR_INVALID_PARAMETER;
|
---|
[67955] | 796 | if (pReq->pSession != pSession)
|
---|
[5031] | 797 | return VERR_INVALID_POINTER;
|
---|
| 798 |
|
---|
| 799 | /*
|
---|
| 800 | * Execute it.
|
---|
| 801 | */
|
---|
[80346] | 802 | PGVM pGVM;
|
---|
[5031] | 803 | pReq->pVMR0 = NULL;
|
---|
| 804 | pReq->pVMR3 = NIL_RTR3PTR;
|
---|
[80346] | 805 | int rc = GVMMR0CreateVM(pSession, pReq->cCpus, &pGVM);
|
---|
[5031] | 806 | if (RT_SUCCESS(rc))
|
---|
| 807 | {
|
---|
[80346] | 808 | pReq->pVMR0 = pGVM; /** @todo don't expose this to ring-3, use a unique random number instead. */
|
---|
| 809 | pReq->pVMR3 = pGVM->pVMR3;
|
---|
[5031] | 810 | }
|
---|
| 811 | return rc;
|
---|
| 812 | }
|
---|
| 813 |
|
---|
| 814 |
|
---|
| 815 | /**
|
---|
[4971] | 816 | * Allocates the VM structure and registers it with GVM.
|
---|
| 817 | *
|
---|
[6801] | 818 | * The caller will become the VM owner and there by the EMT.
|
---|
| 819 | *
|
---|
[4971] | 820 | * @returns VBox status code.
|
---|
| 821 | * @param pSession The support driver session.
|
---|
[19395] | 822 | * @param cCpus Number of virtual CPUs for the new VM.
|
---|
[80346] | 823 | * @param ppGVM Where to store the pointer to the VM structure.
|
---|
[4971] | 824 | *
|
---|
[6801] | 825 | * @thread EMT.
|
---|
[4971] | 826 | */
|
---|
[80346] | 827 | GVMMR0DECL(int) GVMMR0CreateVM(PSUPDRVSESSION pSession, uint32_t cCpus, PGVM *ppGVM)
|
---|
[4971] | 828 | {
|
---|
[5031] | 829 | LogFlow(("GVMMR0CreateVM: pSession=%p\n", pSession));
|
---|
| 830 | PGVMM pGVMM;
|
---|
[39402] | 831 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
[4971] | 832 |
|
---|
[80346] | 833 | AssertPtrReturn(ppGVM, VERR_INVALID_POINTER);
|
---|
| 834 | *ppGVM = NULL;
|
---|
[4971] | 835 |
|
---|
[19395] | 836 | if ( cCpus == 0
|
---|
[19403] | 837 | || cCpus > VMM_MAX_CPU_COUNT)
|
---|
[13796] | 838 | return VERR_INVALID_PARAMETER;
|
---|
| 839 |
|
---|
[19398] | 840 | RTNATIVETHREAD hEMT0 = RTThreadNativeSelf();
|
---|
[39402] | 841 | AssertReturn(hEMT0 != NIL_RTNATIVETHREAD, VERR_GVMM_BROKEN_IPRT);
|
---|
[40595] | 842 | RTPROCESS ProcId = RTProcSelf();
|
---|
[39402] | 843 | AssertReturn(ProcId != NIL_RTPROCESS, VERR_GVMM_BROKEN_IPRT);
|
---|
[4971] | 844 |
|
---|
| 845 | /*
|
---|
| 846 | * The whole allocation process is protected by the lock.
|
---|
| 847 | */
|
---|
[6635] | 848 | int rc = gvmmR0CreateDestroyLock(pGVMM);
|
---|
[4971] | 849 | AssertRCReturn(rc, rc);
|
---|
| 850 |
|
---|
| 851 | /*
|
---|
[67955] | 852 | * Only one VM per session.
|
---|
| 853 | */
|
---|
| 854 | if (SUPR0GetSessionVM(pSession) != NULL)
|
---|
| 855 | {
|
---|
| 856 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
| 857 | SUPR0Printf("GVMMR0CreateVM: The session %p already got a VM: %p\n", pSession, SUPR0GetSessionVM(pSession));
|
---|
| 858 | return VERR_ALREADY_EXISTS;
|
---|
| 859 | }
|
---|
| 860 |
|
---|
| 861 | /*
|
---|
[4971] | 862 | * Allocate a handle first so we don't waste resources unnecessarily.
|
---|
| 863 | */
|
---|
[5026] | 864 | uint16_t iHandle = pGVMM->iFreeHead;
|
---|
[4971] | 865 | if (iHandle)
|
---|
| 866 | {
|
---|
[5031] | 867 | PGVMHANDLE pHandle = &pGVMM->aHandles[iHandle];
|
---|
[4971] | 868 |
|
---|
| 869 | /* consistency checks, a bit paranoid as always. */
|
---|
[80347] | 870 | if ( !pHandle->pGVM
|
---|
[4971] | 871 | && !pHandle->pvObj
|
---|
| 872 | && pHandle->iSelf == iHandle)
|
---|
| 873 | {
|
---|
[5026] | 874 | pHandle->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_VM, gvmmR0HandleObjDestructor, pGVMM, pHandle);
|
---|
[4971] | 875 | if (pHandle->pvObj)
|
---|
| 876 | {
|
---|
| 877 | /*
|
---|
| 878 | * Move the handle from the free to used list and perform permission checks.
|
---|
| 879 | */
|
---|
[58830] | 880 | rc = GVMMR0_USED_EXCLUSIVE_LOCK(pGVMM);
|
---|
[5167] | 881 | AssertRC(rc);
|
---|
| 882 |
|
---|
[5026] | 883 | pGVMM->iFreeHead = pHandle->iNext;
|
---|
| 884 | pHandle->iNext = pGVMM->iUsedHead;
|
---|
| 885 | pGVMM->iUsedHead = iHandle;
|
---|
[5167] | 886 | pGVMM->cVMs++;
|
---|
[4971] | 887 |
|
---|
[19381] | 888 | pHandle->pGVM = NULL;
|
---|
[4971] | 889 | pHandle->pSession = pSession;
|
---|
[19398] | 890 | pHandle->hEMT0 = NIL_RTNATIVETHREAD;
|
---|
| 891 | pHandle->ProcId = NIL_RTPROCESS;
|
---|
[4971] | 892 |
|
---|
[58830] | 893 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
[5167] | 894 |
|
---|
[4971] | 895 | rc = SUPR0ObjVerifyAccess(pHandle->pvObj, pSession, NULL);
|
---|
| 896 | if (RT_SUCCESS(rc))
|
---|
| 897 | {
|
---|
| 898 | /*
|
---|
[78431] | 899 | * Allocate memory for the VM structure (combined VM + GVM).
|
---|
| 900 | */
|
---|
| 901 | const uint32_t cbVM = RT_UOFFSETOF_DYN(GVM, aCpus[cCpus]);
|
---|
[93554] | 902 | const uint32_t cPages = RT_ALIGN_32(cbVM, HOST_PAGE_SIZE) >> HOST_PAGE_SHIFT;
|
---|
[78431] | 903 | RTR0MEMOBJ hVMMemObj = NIL_RTR0MEMOBJ;
|
---|
[93554] | 904 | rc = RTR0MemObjAllocPage(&hVMMemObj, cPages << HOST_PAGE_SHIFT, false /* fExecutable */);
|
---|
[78431] | 905 | if (RT_SUCCESS(rc))
|
---|
| 906 | {
|
---|
| 907 | PGVM pGVM = (PGVM)RTR0MemObjAddress(hVMMemObj);
|
---|
| 908 | AssertPtr(pGVM);
|
---|
| 909 |
|
---|
| 910 | /*
|
---|
| 911 | * Initialise the structure.
|
---|
| 912 | */
|
---|
[93554] | 913 | RT_BZERO(pGVM, cPages << HOST_PAGE_SHIFT);
|
---|
[78431] | 914 | gvmmR0InitPerVMData(pGVM, iHandle, cCpus, pSession);
|
---|
[82555] | 915 | pGVM->gvmm.s.VMMemObj = hVMMemObj;
|
---|
[82989] | 916 | rc = GMMR0InitPerVMData(pGVM);
|
---|
[93596] | 917 | int rc2 = PGMR0InitPerVMData(pGVM, hVMMemObj);
|
---|
[90897] | 918 | int rc3 = VMMR0InitPerVMData(pGVM);
|
---|
[94943] | 919 | CPUMR0InitPerVMData(pGVM);
|
---|
[86683] | 920 | DBGFR0InitPerVMData(pGVM);
|
---|
[80531] | 921 | PDMR0InitPerVMData(pGVM);
|
---|
[80641] | 922 | IOMR0InitPerVMData(pGVM);
|
---|
[87792] | 923 | TMR0InitPerVMData(pGVM);
|
---|
[90897] | 924 | if (RT_SUCCESS(rc) && RT_SUCCESS(rc2) && RT_SUCCESS(rc3))
|
---|
[78431] | 925 | {
|
---|
| 926 | /*
|
---|
[82555] | 927 | * Allocate page array.
|
---|
| 928 | * This currently have to be made available to ring-3, but this is should change eventually.
|
---|
[78431] | 929 | */
|
---|
[82555] | 930 | rc = RTR0MemObjAllocPage(&pGVM->gvmm.s.VMPagesMemObj, cPages * sizeof(SUPPAGE), false /* fExecutable */);
|
---|
[78431] | 931 | if (RT_SUCCESS(rc))
|
---|
| 932 | {
|
---|
[82555] | 933 | PSUPPAGE paPages = (PSUPPAGE)RTR0MemObjAddress(pGVM->gvmm.s.VMPagesMemObj); AssertPtr(paPages);
|
---|
| 934 | for (uint32_t iPage = 0; iPage < cPages; iPage++)
|
---|
| 935 | {
|
---|
| 936 | paPages[iPage].uReserved = 0;
|
---|
| 937 | paPages[iPage].Phys = RTR0MemObjGetPagePhysAddr(pGVM->gvmm.s.VMMemObj, iPage);
|
---|
| 938 | Assert(paPages[iPage].Phys != NIL_RTHCPHYS);
|
---|
| 939 | }
|
---|
| 940 |
|
---|
[78431] | 941 | /*
|
---|
[82555] | 942 | * Map the page array, VM and VMCPU structures into ring-3.
|
---|
[78431] | 943 | */
|
---|
[93554] | 944 | AssertCompileSizeAlignment(VM, HOST_PAGE_SIZE);
|
---|
[82555] | 945 | rc = RTR0MemObjMapUserEx(&pGVM->gvmm.s.VMMapObj, pGVM->gvmm.s.VMMemObj, (RTR3PTR)-1, 0,
|
---|
| 946 | RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS,
|
---|
| 947 | 0 /*offSub*/, sizeof(VM));
|
---|
| 948 | for (VMCPUID i = 0; i < cCpus && RT_SUCCESS(rc); i++)
|
---|
[78431] | 949 | {
|
---|
[93554] | 950 | AssertCompileSizeAlignment(VMCPU, HOST_PAGE_SIZE);
|
---|
[82555] | 951 | rc = RTR0MemObjMapUserEx(&pGVM->aCpus[i].gvmm.s.VMCpuMapObj, pGVM->gvmm.s.VMMemObj,
|
---|
| 952 | (RTR3PTR)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS,
|
---|
| 953 | RT_UOFFSETOF_DYN(GVM, aCpus[i]), sizeof(VMCPU));
|
---|
[78431] | 954 | }
|
---|
[82555] | 955 | if (RT_SUCCESS(rc))
|
---|
| 956 | rc = RTR0MemObjMapUser(&pGVM->gvmm.s.VMPagesMapObj, pGVM->gvmm.s.VMPagesMemObj, (RTR3PTR)-1,
|
---|
| 957 | 0 /* uAlignment */, RTMEM_PROT_READ | RTMEM_PROT_WRITE,
|
---|
| 958 | NIL_RTR0PROCESS);
|
---|
| 959 | if (RT_SUCCESS(rc))
|
---|
| 960 | {
|
---|
| 961 | /*
|
---|
| 962 | * Initialize all the VM pointers.
|
---|
| 963 | */
|
---|
| 964 | PVMR3 pVMR3 = RTR0MemObjAddressR3(pGVM->gvmm.s.VMMapObj);
|
---|
[90824] | 965 | AssertMsg(RTR0MemUserIsValidAddr(pVMR3) && pVMR3 != NIL_RTR3PTR, ("%p\n", pVMR3));
|
---|
[78431] | 966 |
|
---|
[82555] | 967 | for (VMCPUID i = 0; i < cCpus; i++)
|
---|
| 968 | {
|
---|
| 969 | pGVM->aCpus[i].pVMR0 = pGVM;
|
---|
| 970 | pGVM->aCpus[i].pVMR3 = pVMR3;
|
---|
| 971 | pGVM->apCpusR3[i] = RTR0MemObjAddressR3(pGVM->aCpus[i].gvmm.s.VMCpuMapObj);
|
---|
| 972 | pGVM->aCpus[i].pVCpuR3 = pGVM->apCpusR3[i];
|
---|
| 973 | pGVM->apCpusR0[i] = &pGVM->aCpus[i];
|
---|
[90824] | 974 | AssertMsg(RTR0MemUserIsValidAddr(pGVM->apCpusR3[i]) && pGVM->apCpusR3[i] != NIL_RTR3PTR,
|
---|
| 975 | ("apCpusR3[%u]=%p\n", i, pGVM->apCpusR3[i]));
|
---|
[82555] | 976 | }
|
---|
[78431] | 977 |
|
---|
[82555] | 978 | pGVM->paVMPagesR3 = RTR0MemObjAddressR3(pGVM->gvmm.s.VMPagesMapObj);
|
---|
[90824] | 979 | AssertMsg(RTR0MemUserIsValidAddr(pGVM->paVMPagesR3) && pGVM->paVMPagesR3 != NIL_RTR3PTR,
|
---|
| 980 | ("%p\n", pGVM->paVMPagesR3));
|
---|
[78431] | 981 |
|
---|
[92892] | 982 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
[82555] | 983 | /*
|
---|
[92892] | 984 | * Create the high resolution wake-up timer for EMT 0, ignore failures.
|
---|
| 985 | */
|
---|
| 986 | if (RTTimerCanDoHighResolution())
|
---|
| 987 | {
|
---|
| 988 | int rc4 = RTTimerCreateEx(&pGVM->aCpus[0].gvmm.s.hHrWakeUpTimer,
|
---|
| 989 | 0 /*one-shot, no interval*/,
|
---|
| 990 | RTTIMER_FLAGS_HIGH_RES, gvmmR0EmtWakeUpTimerCallback,
|
---|
| 991 | &pGVM->aCpus[0]);
|
---|
| 992 | if (RT_FAILURE(rc4))
|
---|
| 993 | pGVM->aCpus[0].gvmm.s.hHrWakeUpTimer = NULL;
|
---|
| 994 | }
|
---|
| 995 | #endif
|
---|
| 996 |
|
---|
| 997 | /*
|
---|
[82555] | 998 | * Complete the handle - take the UsedLock sem just to be careful.
|
---|
| 999 | */
|
---|
| 1000 | rc = GVMMR0_USED_EXCLUSIVE_LOCK(pGVMM);
|
---|
| 1001 | AssertRC(rc);
|
---|
[78431] | 1002 |
|
---|
[90597] | 1003 | pHandle->pGVM = pGVM;
|
---|
| 1004 | pHandle->hEMT0 = hEMT0;
|
---|
| 1005 | pHandle->ProcId = ProcId;
|
---|
| 1006 | pGVM->pVMR3 = pVMR3;
|
---|
| 1007 | pGVM->pVMR3Unsafe = pVMR3;
|
---|
| 1008 | pGVM->aCpus[0].hEMT = hEMT0;
|
---|
| 1009 | pGVM->aCpus[0].hNativeThreadR0 = hEMT0;
|
---|
| 1010 | pGVM->aCpus[0].cEmtHashCollisions = 0;
|
---|
| 1011 | uint32_t const idxHash = GVMM_EMT_HASH_1(hEMT0);
|
---|
| 1012 | pGVM->aCpus[0].gvmm.s.idxEmtHash = (uint16_t)idxHash;
|
---|
| 1013 | pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt = hEMT0;
|
---|
| 1014 | pGVM->gvmm.s.aEmtHash[idxHash].idVCpu = 0;
|
---|
[82555] | 1015 | pGVMM->cEMTs += cCpus;
|
---|
| 1016 |
|
---|
| 1017 | /* Associate it with the session and create the context hook for EMT0. */
|
---|
| 1018 | rc = SUPR0SetSessionVM(pSession, pGVM, pGVM);
|
---|
[78431] | 1019 | if (RT_SUCCESS(rc))
|
---|
| 1020 | {
|
---|
[82555] | 1021 | rc = VMMR0ThreadCtxHookCreateForEmt(&pGVM->aCpus[0]);
|
---|
| 1022 | if (RT_SUCCESS(rc))
|
---|
| 1023 | {
|
---|
| 1024 | /*
|
---|
| 1025 | * Done!
|
---|
| 1026 | */
|
---|
| 1027 | VBOXVMM_R0_GVMM_VM_CREATED(pGVM, pGVM, ProcId, (void *)hEMT0, cCpus);
|
---|
[78431] | 1028 |
|
---|
[82555] | 1029 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
| 1030 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[78431] | 1031 |
|
---|
[82555] | 1032 | CPUMR0RegisterVCpuThread(&pGVM->aCpus[0]);
|
---|
[78431] | 1033 |
|
---|
[82555] | 1034 | *ppGVM = pGVM;
|
---|
| 1035 | Log(("GVMMR0CreateVM: pVMR3=%p pGVM=%p hGVM=%d\n", pVMR3, pGVM, iHandle));
|
---|
| 1036 | return VINF_SUCCESS;
|
---|
| 1037 | }
|
---|
| 1038 |
|
---|
| 1039 | SUPR0SetSessionVM(pSession, NULL, NULL);
|
---|
[78431] | 1040 | }
|
---|
[82555] | 1041 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
[78431] | 1042 | }
|
---|
| 1043 |
|
---|
[82555] | 1044 | /* Cleanup mappings. */
|
---|
| 1045 | if (pGVM->gvmm.s.VMMapObj != NIL_RTR0MEMOBJ)
|
---|
[78431] | 1046 | {
|
---|
[82555] | 1047 | RTR0MemObjFree(pGVM->gvmm.s.VMMapObj, false /* fFreeMappings */);
|
---|
| 1048 | pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
|
---|
[78431] | 1049 | }
|
---|
[82555] | 1050 | for (VMCPUID i = 0; i < cCpus; i++)
|
---|
| 1051 | if (pGVM->aCpus[i].gvmm.s.VMCpuMapObj != NIL_RTR0MEMOBJ)
|
---|
| 1052 | {
|
---|
| 1053 | RTR0MemObjFree(pGVM->aCpus[i].gvmm.s.VMCpuMapObj, false /* fFreeMappings */);
|
---|
| 1054 | pGVM->aCpus[i].gvmm.s.VMCpuMapObj = NIL_RTR0MEMOBJ;
|
---|
| 1055 | }
|
---|
| 1056 | if (pGVM->gvmm.s.VMPagesMapObj != NIL_RTR0MEMOBJ)
|
---|
| 1057 | {
|
---|
| 1058 | RTR0MemObjFree(pGVM->gvmm.s.VMPagesMapObj, false /* fFreeMappings */);
|
---|
| 1059 | pGVM->gvmm.s.VMPagesMapObj = NIL_RTR0MEMOBJ;
|
---|
| 1060 | }
|
---|
[78431] | 1061 | }
|
---|
| 1062 | }
|
---|
[90897] | 1063 | else
|
---|
| 1064 | {
|
---|
| 1065 | if (RT_SUCCESS_NP(rc))
|
---|
| 1066 | rc = rc2;
|
---|
| 1067 | if (RT_SUCCESS_NP(rc))
|
---|
| 1068 | rc = rc3;
|
---|
[100927] | 1069 | AssertStmt(RT_FAILURE_NP(rc), rc = VERR_IPE_UNEXPECTED_STATUS);
|
---|
[90897] | 1070 | }
|
---|
[78431] | 1071 | }
|
---|
[4971] | 1072 | }
|
---|
| 1073 | /* else: The user wasn't permitted to create this VM. */
|
---|
| 1074 |
|
---|
| 1075 | /*
|
---|
[5026] | 1076 | * The handle will be freed by gvmmR0HandleObjDestructor as we release the
|
---|
[4971] | 1077 | * object reference here. A little extra mess because of non-recursive lock.
|
---|
| 1078 | */
|
---|
| 1079 | void *pvObj = pHandle->pvObj;
|
---|
| 1080 | pHandle->pvObj = NULL;
|
---|
[6635] | 1081 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[4971] | 1082 |
|
---|
| 1083 | SUPR0ObjRelease(pvObj, pSession);
|
---|
| 1084 |
|
---|
[78431] | 1085 | SUPR0Printf("GVMMR0CreateVM: failed, rc=%Rrc\n", rc);
|
---|
[4971] | 1086 | return rc;
|
---|
| 1087 | }
|
---|
| 1088 |
|
---|
| 1089 | rc = VERR_NO_MEMORY;
|
---|
| 1090 | }
|
---|
| 1091 | else
|
---|
[39402] | 1092 | rc = VERR_GVMM_IPE_1;
|
---|
[4971] | 1093 | }
|
---|
| 1094 | else
|
---|
| 1095 | rc = VERR_GVM_TOO_MANY_VMS;
|
---|
| 1096 |
|
---|
[6635] | 1097 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[4971] | 1098 | return rc;
|
---|
| 1099 | }
|
---|
| 1100 |
|
---|
| 1101 |
|
---|
| 1102 | /**
|
---|
[5167] | 1103 | * Initializes the per VM data belonging to GVMM.
|
---|
| 1104 | *
|
---|
| 1105 | * @param pGVM Pointer to the global VM structure.
|
---|
[80336] | 1106 | * @param hSelf The handle.
|
---|
| 1107 | * @param cCpus The CPU count.
|
---|
| 1108 | * @param pSession The session this VM is associated with.
|
---|
[5167] | 1109 | */
|
---|
[78431] | 1110 | static void gvmmR0InitPerVMData(PGVM pGVM, int16_t hSelf, VMCPUID cCpus, PSUPDRVSESSION pSession)
|
---|
[5167] | 1111 | {
|
---|
| 1112 | AssertCompile(RT_SIZEOFMEMB(GVM,gvmm.s) <= RT_SIZEOFMEMB(GVM,gvmm.padding));
|
---|
[19381] | 1113 | AssertCompile(RT_SIZEOFMEMB(GVMCPU,gvmm.s) <= RT_SIZEOFMEMB(GVMCPU,gvmm.padding));
|
---|
[78431] | 1114 | AssertCompileMemberAlignment(VM, cpum, 64);
|
---|
| 1115 | AssertCompileMemberAlignment(VM, tm, 64);
|
---|
| 1116 |
|
---|
| 1117 | /* GVM: */
|
---|
| 1118 | pGVM->u32Magic = GVM_MAGIC;
|
---|
[80274] | 1119 | pGVM->hSelf = hSelf;
|
---|
| 1120 | pGVM->cCpus = cCpus;
|
---|
| 1121 | pGVM->pSession = pSession;
|
---|
[80281] | 1122 | pGVM->pSelf = pGVM;
|
---|
[78431] | 1123 |
|
---|
| 1124 | /* VM: */
|
---|
| 1125 | pGVM->enmVMState = VMSTATE_CREATING;
|
---|
[80281] | 1126 | pGVM->hSelfUnsafe = hSelf;
|
---|
[80274] | 1127 | pGVM->pSessionUnsafe = pSession;
|
---|
[80281] | 1128 | pGVM->pVMR0ForCall = pGVM;
|
---|
[80274] | 1129 | pGVM->cCpusUnsafe = cCpus;
|
---|
[78431] | 1130 | pGVM->uCpuExecutionCap = 100; /* default is no cap. */
|
---|
| 1131 | pGVM->uStructVersion = 1;
|
---|
| 1132 | pGVM->cbSelf = sizeof(VM);
|
---|
| 1133 | pGVM->cbVCpu = sizeof(VMCPU);
|
---|
| 1134 |
|
---|
| 1135 | /* GVMM: */
|
---|
| 1136 | pGVM->gvmm.s.VMMemObj = NIL_RTR0MEMOBJ;
|
---|
| 1137 | pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
|
---|
| 1138 | pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
|
---|
| 1139 | pGVM->gvmm.s.VMPagesMapObj = NIL_RTR0MEMOBJ;
|
---|
[14811] | 1140 | pGVM->gvmm.s.fDoneVMMR0Init = false;
|
---|
| 1141 | pGVM->gvmm.s.fDoneVMMR0Term = false;
|
---|
[92229] | 1142 |
|
---|
| 1143 | for (size_t i = 0; i < RT_ELEMENTS(pGVM->gvmm.s.aWorkerThreads); i++)
|
---|
| 1144 | {
|
---|
| 1145 | pGVM->gvmm.s.aWorkerThreads[i].hNativeThread = NIL_RTNATIVETHREAD;
|
---|
| 1146 | pGVM->gvmm.s.aWorkerThreads[i].hNativeThreadR3 = NIL_RTNATIVETHREAD;
|
---|
| 1147 | }
|
---|
| 1148 | pGVM->gvmm.s.aWorkerThreads[0].hNativeThread = GVMM_RTNATIVETHREAD_DESTROYED; /* invalid entry */
|
---|
| 1149 |
|
---|
[90597] | 1150 | for (size_t i = 0; i < RT_ELEMENTS(pGVM->gvmm.s.aEmtHash); i++)
|
---|
| 1151 | {
|
---|
| 1152 | pGVM->gvmm.s.aEmtHash[i].hNativeEmt = NIL_RTNATIVETHREAD;
|
---|
| 1153 | pGVM->gvmm.s.aEmtHash[i].idVCpu = NIL_VMCPUID;
|
---|
| 1154 | }
|
---|
[19360] | 1155 |
|
---|
[78431] | 1156 | /*
|
---|
| 1157 | * Per virtual CPU.
|
---|
| 1158 | */
|
---|
[19395] | 1159 | for (VMCPUID i = 0; i < pGVM->cCpus; i++)
|
---|
[19381] | 1160 | {
|
---|
[72428] | 1161 | pGVM->aCpus[i].idCpu = i;
|
---|
[80274] | 1162 | pGVM->aCpus[i].idCpuUnsafe = i;
|
---|
[19360] | 1163 | pGVM->aCpus[i].gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
|
---|
[78431] | 1164 | pGVM->aCpus[i].gvmm.s.VMCpuMapObj = NIL_RTR0MEMOBJ;
|
---|
[90597] | 1165 | pGVM->aCpus[i].gvmm.s.idxEmtHash = UINT16_MAX;
|
---|
[92892] | 1166 | pGVM->aCpus[i].gvmm.s.hHrWakeUpTimer = NULL;
|
---|
[19381] | 1167 | pGVM->aCpus[i].hEMT = NIL_RTNATIVETHREAD;
|
---|
[71222] | 1168 | pGVM->aCpus[i].pGVM = pGVM;
|
---|
[78431] | 1169 | pGVM->aCpus[i].idHostCpu = NIL_RTCPUID;
|
---|
| 1170 | pGVM->aCpus[i].iHostCpuSet = UINT32_MAX;
|
---|
| 1171 | pGVM->aCpus[i].hNativeThread = NIL_RTNATIVETHREAD;
|
---|
| 1172 | pGVM->aCpus[i].hNativeThreadR0 = NIL_RTNATIVETHREAD;
|
---|
| 1173 | pGVM->aCpus[i].enmState = VMCPUSTATE_STOPPED;
|
---|
[80319] | 1174 | pGVM->aCpus[i].pVCpuR0ForVtg = &pGVM->aCpus[i];
|
---|
[19381] | 1175 | }
|
---|
[5167] | 1176 | }
|
---|
| 1177 |
|
---|
| 1178 |
|
---|
| 1179 | /**
|
---|
| 1180 | * Does the VM initialization.
|
---|
| 1181 | *
|
---|
| 1182 | * @returns VBox status code.
|
---|
[68011] | 1183 | * @param pGVM The global (ring-0) VM structure.
|
---|
[5167] | 1184 | */
|
---|
[68011] | 1185 | GVMMR0DECL(int) GVMMR0InitVM(PGVM pGVM)
|
---|
[5167] | 1186 | {
|
---|
[68011] | 1187 | LogFlow(("GVMMR0InitVM: pGVM=%p\n", pGVM));
|
---|
[5167] | 1188 |
|
---|
[68011] | 1189 | int rc = VERR_INTERNAL_ERROR_3;
|
---|
| 1190 | if ( !pGVM->gvmm.s.fDoneVMMR0Init
|
---|
| 1191 | && pGVM->aCpus[0].gvmm.s.HaltEventMulti == NIL_RTSEMEVENTMULTI)
|
---|
[5167] | 1192 | {
|
---|
[68011] | 1193 | for (VMCPUID i = 0; i < pGVM->cCpus; i++)
|
---|
[5167] | 1194 | {
|
---|
[68011] | 1195 | rc = RTSemEventMultiCreate(&pGVM->aCpus[i].gvmm.s.HaltEventMulti);
|
---|
| 1196 | if (RT_FAILURE(rc))
|
---|
[19360] | 1197 | {
|
---|
[68011] | 1198 | pGVM->aCpus[i].gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
|
---|
| 1199 | break;
|
---|
[19360] | 1200 | }
|
---|
[5167] | 1201 | }
|
---|
| 1202 | }
|
---|
[68011] | 1203 | else
|
---|
| 1204 | rc = VERR_WRONG_ORDER;
|
---|
[5167] | 1205 |
|
---|
| 1206 | LogFlow(("GVMMR0InitVM: returns %Rrc\n", rc));
|
---|
| 1207 | return rc;
|
---|
| 1208 | }
|
---|
| 1209 |
|
---|
| 1210 |
|
---|
| 1211 | /**
|
---|
[14811] | 1212 | * Indicates that we're done with the ring-0 initialization
|
---|
| 1213 | * of the VM.
|
---|
| 1214 | *
|
---|
[68011] | 1215 | * @param pGVM The global (ring-0) VM structure.
|
---|
[19395] | 1216 | * @thread EMT(0)
|
---|
[14811] | 1217 | */
|
---|
[68011] | 1218 | GVMMR0DECL(void) GVMMR0DoneInitVM(PGVM pGVM)
|
---|
[14811] | 1219 | {
|
---|
| 1220 | /* Set the indicator. */
|
---|
| 1221 | pGVM->gvmm.s.fDoneVMMR0Init = true;
|
---|
| 1222 | }
|
---|
| 1223 |
|
---|
| 1224 |
|
---|
| 1225 | /**
|
---|
| 1226 | * Indicates that we're doing the ring-0 termination of the VM.
|
---|
| 1227 | *
|
---|
| 1228 | * @returns true if termination hasn't been done already, false if it has.
|
---|
| 1229 | * @param pGVM Pointer to the global VM structure. Optional.
|
---|
[68011] | 1230 | * @thread EMT(0) or session cleanup thread.
|
---|
[14811] | 1231 | */
|
---|
[68011] | 1232 | GVMMR0DECL(bool) GVMMR0DoingTermVM(PGVM pGVM)
|
---|
[14811] | 1233 | {
|
---|
| 1234 | /* Validate the VM structure, state and handle. */
|
---|
[68011] | 1235 | AssertPtrReturn(pGVM, false);
|
---|
[14811] | 1236 |
|
---|
| 1237 | /* Set the indicator. */
|
---|
| 1238 | if (pGVM->gvmm.s.fDoneVMMR0Term)
|
---|
| 1239 | return false;
|
---|
| 1240 | pGVM->gvmm.s.fDoneVMMR0Term = true;
|
---|
| 1241 | return true;
|
---|
| 1242 | }
|
---|
| 1243 |
|
---|
| 1244 |
|
---|
| 1245 | /**
|
---|
[5031] | 1246 | * Destroys the VM, freeing all associated resources (the ring-0 ones anyway).
|
---|
[4971] | 1247 | *
|
---|
[5031] | 1248 | * This is call from the vmR3DestroyFinalBit and from a error path in VMR3Create,
|
---|
| 1249 | * and the caller is not the EMT thread, unfortunately. For security reasons, it
|
---|
| 1250 | * would've been nice if the caller was actually the EMT thread or that we somehow
|
---|
| 1251 | * could've associated the calling thread with the VM up front.
|
---|
| 1252 | *
|
---|
[4971] | 1253 | * @returns VBox status code.
|
---|
[67955] | 1254 | * @param pGVM The global (ring-0) VM structure.
|
---|
[4971] | 1255 | *
|
---|
[19395] | 1256 | * @thread EMT(0) if it's associated with the VM, otherwise any thread.
|
---|
[4971] | 1257 | */
|
---|
[80346] | 1258 | GVMMR0DECL(int) GVMMR0DestroyVM(PGVM pGVM)
|
---|
[4971] | 1259 | {
|
---|
[80346] | 1260 | LogFlow(("GVMMR0DestroyVM: pGVM=%p\n", pGVM));
|
---|
[5031] | 1261 | PGVMM pGVMM;
|
---|
[39402] | 1262 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
[4971] | 1263 |
|
---|
| 1264 | /*
|
---|
| 1265 | * Validate the VM structure, state and caller.
|
---|
| 1266 | */
|
---|
[67955] | 1267 | AssertPtrReturn(pGVM, VERR_INVALID_POINTER);
|
---|
[93554] | 1268 | AssertReturn(!((uintptr_t)pGVM & HOST_PAGE_OFFSET_MASK), VERR_INVALID_POINTER);
|
---|
[80346] | 1269 | AssertMsgReturn(pGVM->enmVMState >= VMSTATE_CREATING && pGVM->enmVMState <= VMSTATE_TERMINATED, ("%d\n", pGVM->enmVMState),
|
---|
[48208] | 1270 | VERR_WRONG_ORDER);
|
---|
[4971] | 1271 |
|
---|
[67956] | 1272 | uint32_t hGVM = pGVM->hSelf;
|
---|
[71152] | 1273 | ASMCompilerBarrier();
|
---|
[67987] | 1274 | AssertReturn(hGVM != NIL_GVM_HANDLE, VERR_INVALID_VM_HANDLE);
|
---|
| 1275 | AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_VM_HANDLE);
|
---|
[4971] | 1276 |
|
---|
[67956] | 1277 | PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
|
---|
[80346] | 1278 | AssertReturn(pHandle->pGVM == pGVM, VERR_NOT_OWNER);
|
---|
[4971] | 1279 |
|
---|
[67956] | 1280 | RTPROCESS ProcId = RTProcSelf();
|
---|
| 1281 | RTNATIVETHREAD hSelf = RTThreadNativeSelf();
|
---|
[19398] | 1282 | AssertReturn( ( pHandle->hEMT0 == hSelf
|
---|
| 1283 | && pHandle->ProcId == ProcId)
|
---|
| 1284 | || pHandle->hEMT0 == NIL_RTNATIVETHREAD, VERR_NOT_OWNER);
|
---|
[4971] | 1285 |
|
---|
| 1286 | /*
|
---|
| 1287 | * Lookup the handle and destroy the object.
|
---|
[5031] | 1288 | * Since the lock isn't recursive and we'll have to leave it before dereferencing the
|
---|
| 1289 | * object, we take some precautions against racing callers just in case...
|
---|
[4971] | 1290 | */
|
---|
[6635] | 1291 | int rc = gvmmR0CreateDestroyLock(pGVMM);
|
---|
[4971] | 1292 | AssertRC(rc);
|
---|
| 1293 |
|
---|
[47456] | 1294 | /* Be careful here because we might theoretically be racing someone else cleaning up. */
|
---|
[80346] | 1295 | if ( pHandle->pGVM == pGVM
|
---|
[71222] | 1296 | && ( ( pHandle->hEMT0 == hSelf
|
---|
| 1297 | && pHandle->ProcId == ProcId)
|
---|
| 1298 | || pHandle->hEMT0 == NIL_RTNATIVETHREAD)
|
---|
[80346] | 1299 | && RT_VALID_PTR(pHandle->pvObj)
|
---|
| 1300 | && RT_VALID_PTR(pHandle->pSession)
|
---|
| 1301 | && RT_VALID_PTR(pHandle->pGVM)
|
---|
[71222] | 1302 | && pHandle->pGVM->u32Magic == GVM_MAGIC)
|
---|
[4971] | 1303 | {
|
---|
[67987] | 1304 | /* Check that other EMTs have deregistered. */
|
---|
| 1305 | uint32_t cNotDeregistered = 0;
|
---|
| 1306 | for (VMCPUID idCpu = 1; idCpu < pGVM->cCpus; idCpu++)
|
---|
[90597] | 1307 | cNotDeregistered += pGVM->aCpus[idCpu].hEMT != GVMM_RTNATIVETHREAD_DESTROYED;
|
---|
[67987] | 1308 | if (cNotDeregistered == 0)
|
---|
| 1309 | {
|
---|
| 1310 | /* Grab the object pointer. */
|
---|
| 1311 | void *pvObj = pHandle->pvObj;
|
---|
| 1312 | pHandle->pvObj = NULL;
|
---|
| 1313 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[4971] | 1314 |
|
---|
[67987] | 1315 | SUPR0ObjRelease(pvObj, pHandle->pSession);
|
---|
| 1316 | }
|
---|
| 1317 | else
|
---|
[48395] | 1318 | {
|
---|
[67987] | 1319 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
| 1320 | rc = VERR_GVMM_NOT_ALL_EMTS_DEREGISTERED;
|
---|
[48395] | 1321 | }
|
---|
[4971] | 1322 | }
|
---|
| 1323 | else
|
---|
| 1324 | {
|
---|
[80346] | 1325 | SUPR0Printf("GVMMR0DestroyVM: pHandle=%RKv:{.pGVM=%p, .hEMT0=%p, .ProcId=%u, .pvObj=%p} pGVM=%p hSelf=%p\n",
|
---|
| 1326 | pHandle, pHandle->pGVM, pHandle->hEMT0, pHandle->ProcId, pHandle->pvObj, pGVM, hSelf);
|
---|
[6635] | 1327 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[39402] | 1328 | rc = VERR_GVMM_IPE_2;
|
---|
[4971] | 1329 | }
|
---|
| 1330 |
|
---|
| 1331 | return rc;
|
---|
| 1332 | }
|
---|
| 1333 |
|
---|
| 1334 |
|
---|
| 1335 | /**
|
---|
[14811] | 1336 | * Performs VM cleanup task as part of object destruction.
|
---|
| 1337 | *
|
---|
| 1338 | * @param pGVM The GVM pointer.
|
---|
| 1339 | */
|
---|
[18213] | 1340 | static void gvmmR0CleanupVM(PGVM pGVM)
|
---|
[14811] | 1341 | {
|
---|
| 1342 | if ( pGVM->gvmm.s.fDoneVMMR0Init
|
---|
| 1343 | && !pGVM->gvmm.s.fDoneVMMR0Term)
|
---|
| 1344 | {
|
---|
| 1345 | if ( pGVM->gvmm.s.VMMemObj != NIL_RTR0MEMOBJ
|
---|
[80334] | 1346 | && RTR0MemObjAddress(pGVM->gvmm.s.VMMemObj) == pGVM)
|
---|
[14811] | 1347 | {
|
---|
[18213] | 1348 | LogFlow(("gvmmR0CleanupVM: Calling VMMR0TermVM\n"));
|
---|
[80346] | 1349 | VMMR0TermVM(pGVM, NIL_VMCPUID);
|
---|
[14811] | 1350 | }
|
---|
| 1351 | else
|
---|
[78431] | 1352 | AssertMsgFailed(("gvmmR0CleanupVM: VMMemObj=%p pGVM=%p\n", pGVM->gvmm.s.VMMemObj, pGVM));
|
---|
[14811] | 1353 | }
|
---|
[18213] | 1354 |
|
---|
| 1355 | GMMR0CleanupVM(pGVM);
|
---|
[71075] | 1356 | #ifdef VBOX_WITH_NEM_R0
|
---|
| 1357 | NEMR0CleanupVM(pGVM);
|
---|
| 1358 | #endif
|
---|
[80531] | 1359 | PDMR0CleanupVM(pGVM);
|
---|
[80641] | 1360 | IOMR0CleanupVM(pGVM);
|
---|
[86683] | 1361 | DBGFR0CleanupVM(pGVM);
|
---|
[82555] | 1362 | PGMR0CleanupVM(pGVM);
|
---|
[87792] | 1363 | TMR0CleanupVM(pGVM);
|
---|
[90829] | 1364 | VMMR0CleanupVM(pGVM);
|
---|
[14811] | 1365 | }
|
---|
| 1366 |
|
---|
| 1367 |
|
---|
| 1368 | /**
|
---|
[58126] | 1369 | * @callback_method_impl{FNSUPDRVDESTRUCTOR,VM handle destructor}
|
---|
[4971] | 1370 | *
|
---|
[58126] | 1371 | * pvUser1 is the GVM instance pointer.
|
---|
| 1372 | * pvUser2 is the handle pointer.
|
---|
[4971] | 1373 | */
|
---|
[58126] | 1374 | static DECLCALLBACK(void) gvmmR0HandleObjDestructor(void *pvObj, void *pvUser1, void *pvUser2)
|
---|
[4971] | 1375 | {
|
---|
[58126] | 1376 | LogFlow(("gvmmR0HandleObjDestructor: %p %p %p\n", pvObj, pvUser1, pvUser2));
|
---|
[4971] | 1377 |
|
---|
[57857] | 1378 | NOREF(pvObj);
|
---|
| 1379 |
|
---|
[4971] | 1380 | /*
|
---|
| 1381 | * Some quick, paranoid, input validation.
|
---|
| 1382 | */
|
---|
[58126] | 1383 | PGVMHANDLE pHandle = (PGVMHANDLE)pvUser2;
|
---|
[4971] | 1384 | AssertPtr(pHandle);
|
---|
[58126] | 1385 | PGVMM pGVMM = (PGVMM)pvUser1;
|
---|
[5026] | 1386 | Assert(pGVMM == g_pGVMM);
|
---|
| 1387 | const uint16_t iHandle = pHandle - &pGVMM->aHandles[0];
|
---|
[4971] | 1388 | if ( !iHandle
|
---|
[5026] | 1389 | || iHandle >= RT_ELEMENTS(pGVMM->aHandles)
|
---|
[4971] | 1390 | || iHandle != pHandle->iSelf)
|
---|
| 1391 | {
|
---|
| 1392 | SUPR0Printf("GVM: handle %d is out of range or corrupt (iSelf=%d)!\n", iHandle, pHandle->iSelf);
|
---|
| 1393 | return;
|
---|
| 1394 | }
|
---|
| 1395 |
|
---|
[6635] | 1396 | int rc = gvmmR0CreateDestroyLock(pGVMM);
|
---|
[4971] | 1397 | AssertRC(rc);
|
---|
[58830] | 1398 | rc = GVMMR0_USED_EXCLUSIVE_LOCK(pGVMM);
|
---|
[5167] | 1399 | AssertRC(rc);
|
---|
[4971] | 1400 |
|
---|
| 1401 | /*
|
---|
[33540] | 1402 | * This is a tad slow but a doubly linked list is too much hassle.
|
---|
[4971] | 1403 | */
|
---|
[5026] | 1404 | if (RT_UNLIKELY(pHandle->iNext >= RT_ELEMENTS(pGVMM->aHandles)))
|
---|
[4971] | 1405 | {
|
---|
| 1406 | SUPR0Printf("GVM: used list index %d is out of range!\n", pHandle->iNext);
|
---|
[58830] | 1407 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
[6635] | 1408 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[4971] | 1409 | return;
|
---|
| 1410 | }
|
---|
| 1411 |
|
---|
[5026] | 1412 | if (pGVMM->iUsedHead == iHandle)
|
---|
| 1413 | pGVMM->iUsedHead = pHandle->iNext;
|
---|
[4971] | 1414 | else
|
---|
| 1415 | {
|
---|
[5026] | 1416 | uint16_t iPrev = pGVMM->iUsedHead;
|
---|
| 1417 | int c = RT_ELEMENTS(pGVMM->aHandles) + 2;
|
---|
[7034] | 1418 | while (iPrev)
|
---|
[4971] | 1419 | {
|
---|
[5026] | 1420 | if (RT_UNLIKELY(iPrev >= RT_ELEMENTS(pGVMM->aHandles)))
|
---|
[4971] | 1421 | {
|
---|
[21915] | 1422 | SUPR0Printf("GVM: used list index %d is out of range!\n", iPrev);
|
---|
[58830] | 1423 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
[6635] | 1424 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[4971] | 1425 | return;
|
---|
| 1426 | }
|
---|
| 1427 | if (RT_UNLIKELY(c-- <= 0))
|
---|
| 1428 | {
|
---|
| 1429 | iPrev = 0;
|
---|
| 1430 | break;
|
---|
| 1431 | }
|
---|
| 1432 |
|
---|
[5026] | 1433 | if (pGVMM->aHandles[iPrev].iNext == iHandle)
|
---|
[4971] | 1434 | break;
|
---|
[5026] | 1435 | iPrev = pGVMM->aHandles[iPrev].iNext;
|
---|
[4971] | 1436 | }
|
---|
| 1437 | if (!iPrev)
|
---|
| 1438 | {
|
---|
| 1439 | SUPR0Printf("GVM: can't find the handle previous previous of %d!\n", pHandle->iSelf);
|
---|
[58830] | 1440 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
[6635] | 1441 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[4971] | 1442 | return;
|
---|
| 1443 | }
|
---|
| 1444 |
|
---|
[7034] | 1445 | Assert(pGVMM->aHandles[iPrev].iNext == iHandle);
|
---|
[5026] | 1446 | pGVMM->aHandles[iPrev].iNext = pHandle->iNext;
|
---|
[4971] | 1447 | }
|
---|
| 1448 | pHandle->iNext = 0;
|
---|
[5167] | 1449 | pGVMM->cVMs--;
|
---|
[4971] | 1450 |
|
---|
| 1451 | /*
|
---|
[5031] | 1452 | * Do the global cleanup round.
|
---|
[4971] | 1453 | */
|
---|
[5031] | 1454 | PGVM pGVM = pHandle->pGVM;
|
---|
[80346] | 1455 | if ( RT_VALID_PTR(pGVM)
|
---|
[71222] | 1456 | && pGVM->u32Magic == GVM_MAGIC)
|
---|
[4971] | 1457 | {
|
---|
[19707] | 1458 | pGVMM->cEMTs -= pGVM->cCpus;
|
---|
[67955] | 1459 |
|
---|
| 1460 | if (pGVM->pSession)
|
---|
| 1461 | SUPR0SetSessionVM(pGVM->pSession, NULL, NULL);
|
---|
| 1462 |
|
---|
[58830] | 1463 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
[19707] | 1464 |
|
---|
[18213] | 1465 | gvmmR0CleanupVM(pGVM);
|
---|
[4971] | 1466 |
|
---|
| 1467 | /*
|
---|
[5031] | 1468 | * Do the GVMM cleanup - must be done last.
|
---|
[4971] | 1469 | */
|
---|
[5031] | 1470 | /* The VM and VM pages mappings/allocations. */
|
---|
| 1471 | if (pGVM->gvmm.s.VMPagesMapObj != NIL_RTR0MEMOBJ)
|
---|
| 1472 | {
|
---|
| 1473 | rc = RTR0MemObjFree(pGVM->gvmm.s.VMPagesMapObj, false /* fFreeMappings */); AssertRC(rc);
|
---|
| 1474 | pGVM->gvmm.s.VMPagesMapObj = NIL_RTR0MEMOBJ;
|
---|
| 1475 | }
|
---|
| 1476 |
|
---|
| 1477 | if (pGVM->gvmm.s.VMMapObj != NIL_RTR0MEMOBJ)
|
---|
| 1478 | {
|
---|
| 1479 | rc = RTR0MemObjFree(pGVM->gvmm.s.VMMapObj, false /* fFreeMappings */); AssertRC(rc);
|
---|
| 1480 | pGVM->gvmm.s.VMMapObj = NIL_RTR0MEMOBJ;
|
---|
| 1481 | }
|
---|
| 1482 |
|
---|
| 1483 | if (pGVM->gvmm.s.VMPagesMemObj != NIL_RTR0MEMOBJ)
|
---|
| 1484 | {
|
---|
| 1485 | rc = RTR0MemObjFree(pGVM->gvmm.s.VMPagesMemObj, false /* fFreeMappings */); AssertRC(rc);
|
---|
| 1486 | pGVM->gvmm.s.VMPagesMemObj = NIL_RTR0MEMOBJ;
|
---|
| 1487 | }
|
---|
| 1488 |
|
---|
[19395] | 1489 | for (VMCPUID i = 0; i < pGVM->cCpus; i++)
|
---|
[12310] | 1490 | {
|
---|
[19360] | 1491 | if (pGVM->aCpus[i].gvmm.s.HaltEventMulti != NIL_RTSEMEVENTMULTI)
|
---|
| 1492 | {
|
---|
| 1493 | rc = RTSemEventMultiDestroy(pGVM->aCpus[i].gvmm.s.HaltEventMulti); AssertRC(rc);
|
---|
| 1494 | pGVM->aCpus[i].gvmm.s.HaltEventMulti = NIL_RTSEMEVENTMULTI;
|
---|
| 1495 | }
|
---|
[78431] | 1496 | if (pGVM->aCpus[i].gvmm.s.VMCpuMapObj != NIL_RTR0MEMOBJ)
|
---|
| 1497 | {
|
---|
| 1498 | rc = RTR0MemObjFree(pGVM->aCpus[i].gvmm.s.VMCpuMapObj, false /* fFreeMappings */); AssertRC(rc);
|
---|
| 1499 | pGVM->aCpus[i].gvmm.s.VMCpuMapObj = NIL_RTR0MEMOBJ;
|
---|
| 1500 | }
|
---|
[92892] | 1501 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 1502 | if (pGVM->aCpus[i].gvmm.s.hHrWakeUpTimer != NULL)
|
---|
| 1503 | {
|
---|
| 1504 | RTTimerDestroy(pGVM->aCpus[i].gvmm.s.hHrWakeUpTimer);
|
---|
| 1505 | pGVM->aCpus[i].gvmm.s.hHrWakeUpTimer = NULL;
|
---|
| 1506 | }
|
---|
| 1507 | #endif
|
---|
[12310] | 1508 | }
|
---|
| 1509 |
|
---|
[5031] | 1510 | /* the GVM structure itself. */
|
---|
[14821] | 1511 | pGVM->u32Magic |= UINT32_C(0x80000000);
|
---|
[78431] | 1512 | Assert(pGVM->gvmm.s.VMMemObj != NIL_RTR0MEMOBJ);
|
---|
| 1513 | rc = RTR0MemObjFree(pGVM->gvmm.s.VMMemObj, true /*fFreeMappings*/); AssertRC(rc);
|
---|
| 1514 | pGVM = NULL;
|
---|
[19707] | 1515 |
|
---|
| 1516 | /* Re-acquire the UsedLock before freeing the handle since we're updating handle fields. */
|
---|
[58830] | 1517 | rc = GVMMR0_USED_EXCLUSIVE_LOCK(pGVMM);
|
---|
[19707] | 1518 | AssertRC(rc);
|
---|
[4971] | 1519 | }
|
---|
[19707] | 1520 | /* else: GVMMR0CreateVM cleanup. */
|
---|
[4971] | 1521 |
|
---|
| 1522 | /*
|
---|
| 1523 | * Free the handle.
|
---|
| 1524 | */
|
---|
[5026] | 1525 | pHandle->iNext = pGVMM->iFreeHead;
|
---|
| 1526 | pGVMM->iFreeHead = iHandle;
|
---|
[30112] | 1527 | ASMAtomicWriteNullPtr(&pHandle->pGVM);
|
---|
| 1528 | ASMAtomicWriteNullPtr(&pHandle->pvObj);
|
---|
| 1529 | ASMAtomicWriteNullPtr(&pHandle->pSession);
|
---|
[37212] | 1530 | ASMAtomicWriteHandle(&pHandle->hEMT0, NIL_RTNATIVETHREAD);
|
---|
[39038] | 1531 | ASMAtomicWriteU32(&pHandle->ProcId, NIL_RTPROCESS);
|
---|
[4971] | 1532 |
|
---|
[58830] | 1533 | GVMMR0_USED_EXCLUSIVE_UNLOCK(pGVMM);
|
---|
[6635] | 1534 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[5211] | 1535 | LogFlow(("gvmmR0HandleObjDestructor: returns\n"));
|
---|
[4971] | 1536 | }
|
---|
| 1537 |
|
---|
[19385] | 1538 |
|
---|
[19382] | 1539 | /**
|
---|
[19385] | 1540 | * Registers the calling thread as the EMT of a Virtual CPU.
|
---|
[19382] | 1541 | *
|
---|
[19385] | 1542 | * Note that VCPU 0 is automatically registered during VM creation.
|
---|
| 1543 | *
|
---|
[19382] | 1544 | * @returns VBox status code
|
---|
[67955] | 1545 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 1546 | * @param idCpu VCPU id to register the current thread as.
|
---|
[19382] | 1547 | */
|
---|
[80346] | 1548 | GVMMR0DECL(int) GVMMR0RegisterVCpu(PGVM pGVM, VMCPUID idCpu)
|
---|
[19382] | 1549 | {
|
---|
[67987] | 1550 | AssertReturn(idCpu != 0, VERR_INVALID_FUNCTION);
|
---|
[4971] | 1551 |
|
---|
[19382] | 1552 | /*
|
---|
| 1553 | * Validate the VM structure, state and handle.
|
---|
| 1554 | */
|
---|
[19385] | 1555 | PGVMM pGVMM;
|
---|
[90597] | 1556 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, false /* fTakeUsedLock */);
|
---|
[67955] | 1557 | if (RT_SUCCESS(rc))
|
---|
| 1558 | {
|
---|
| 1559 | if (idCpu < pGVM->cCpus)
|
---|
| 1560 | {
|
---|
[92892] | 1561 | PGVMCPU const pGVCpu = &pGVM->aCpus[idCpu];
|
---|
[90597] | 1562 | RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
|
---|
| 1563 |
|
---|
| 1564 | gvmmR0CreateDestroyLock(pGVMM); /** @todo per-VM lock? */
|
---|
| 1565 |
|
---|
[67955] | 1566 | /* Check that the EMT isn't already assigned to a thread. */
|
---|
[92892] | 1567 | if (pGVCpu->hEMT == NIL_RTNATIVETHREAD)
|
---|
[67955] | 1568 | {
|
---|
[92892] | 1569 | Assert(pGVCpu->hNativeThreadR0 == NIL_RTNATIVETHREAD);
|
---|
[19382] | 1570 |
|
---|
[90597] | 1571 | /* A thread may only be one EMT (this makes sure hNativeSelf isn't NIL). */
|
---|
[67955] | 1572 | for (VMCPUID iCpu = 0; iCpu < pGVM->cCpus; iCpu++)
|
---|
| 1573 | AssertBreakStmt(pGVM->aCpus[iCpu].hEMT != hNativeSelf, rc = VERR_INVALID_PARAMETER);
|
---|
| 1574 | if (RT_SUCCESS(rc))
|
---|
| 1575 | {
|
---|
| 1576 | /*
|
---|
| 1577 | * Do the assignment, then try setup the hook. Undo if that fails.
|
---|
| 1578 | */
|
---|
[90597] | 1579 | unsigned cCollisions = 0;
|
---|
| 1580 | uint32_t idxHash = GVMM_EMT_HASH_1(hNativeSelf);
|
---|
| 1581 | if (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt != NIL_RTNATIVETHREAD)
|
---|
| 1582 | {
|
---|
| 1583 | uint32_t const idxHash2 = GVMM_EMT_HASH_2(hNativeSelf);
|
---|
| 1584 | do
|
---|
| 1585 | {
|
---|
| 1586 | cCollisions++;
|
---|
| 1587 | Assert(cCollisions < GVMM_EMT_HASH_SIZE);
|
---|
| 1588 | idxHash = (idxHash + idxHash2) % GVMM_EMT_HASH_SIZE;
|
---|
| 1589 | } while (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt != NIL_RTNATIVETHREAD);
|
---|
| 1590 | }
|
---|
| 1591 | pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt = hNativeSelf;
|
---|
| 1592 | pGVM->gvmm.s.aEmtHash[idxHash].idVCpu = idCpu;
|
---|
[78431] | 1593 |
|
---|
[92892] | 1594 | pGVCpu->hNativeThreadR0 = hNativeSelf;
|
---|
| 1595 | pGVCpu->hEMT = hNativeSelf;
|
---|
| 1596 | pGVCpu->cEmtHashCollisions = (uint8_t)cCollisions;
|
---|
| 1597 | pGVCpu->gvmm.s.idxEmtHash = (uint16_t)idxHash;
|
---|
| 1598 |
|
---|
| 1599 | rc = VMMR0ThreadCtxHookCreateForEmt(pGVCpu);
|
---|
[78431] | 1600 | if (RT_SUCCESS(rc))
|
---|
[92892] | 1601 | {
|
---|
| 1602 | CPUMR0RegisterVCpuThread(pGVCpu);
|
---|
| 1603 |
|
---|
| 1604 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 1605 | /*
|
---|
| 1606 | * Create the high resolution wake-up timer, ignore failures.
|
---|
| 1607 | */
|
---|
| 1608 | if (RTTimerCanDoHighResolution())
|
---|
| 1609 | {
|
---|
| 1610 | int rc2 = RTTimerCreateEx(&pGVCpu->gvmm.s.hHrWakeUpTimer, 0 /*one-shot, no interval*/,
|
---|
| 1611 | RTTIMER_FLAGS_HIGH_RES, gvmmR0EmtWakeUpTimerCallback, pGVCpu);
|
---|
| 1612 | if (RT_FAILURE(rc2))
|
---|
| 1613 | pGVCpu->gvmm.s.hHrWakeUpTimer = NULL;
|
---|
| 1614 | }
|
---|
| 1615 | #endif
|
---|
| 1616 | }
|
---|
[78431] | 1617 | else
|
---|
[90597] | 1618 | {
|
---|
[92892] | 1619 | pGVCpu->hNativeThreadR0 = NIL_RTNATIVETHREAD;
|
---|
| 1620 | pGVCpu->hEMT = NIL_RTNATIVETHREAD;
|
---|
[90597] | 1621 | pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt = NIL_RTNATIVETHREAD;
|
---|
| 1622 | pGVM->gvmm.s.aEmtHash[idxHash].idVCpu = NIL_VMCPUID;
|
---|
[92892] | 1623 | pGVCpu->gvmm.s.idxEmtHash = UINT16_MAX;
|
---|
[90597] | 1624 | }
|
---|
[67955] | 1625 | }
|
---|
| 1626 | }
|
---|
| 1627 | else
|
---|
| 1628 | rc = VERR_ACCESS_DENIED;
|
---|
[90597] | 1629 |
|
---|
| 1630 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
[67955] | 1631 | }
|
---|
| 1632 | else
|
---|
| 1633 | rc = VERR_INVALID_CPU_ID;
|
---|
| 1634 | }
|
---|
[61144] | 1635 | return rc;
|
---|
[19382] | 1636 | }
|
---|
| 1637 |
|
---|
[19385] | 1638 |
|
---|
[4971] | 1639 | /**
|
---|
[67987] | 1640 | * Deregisters the calling thread as the EMT of a Virtual CPU.
|
---|
| 1641 | *
|
---|
| 1642 | * Note that VCPU 0 shall call GVMMR0DestroyVM intead of this API.
|
---|
| 1643 | *
|
---|
| 1644 | * @returns VBox status code
|
---|
| 1645 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 1646 | * @param idCpu VCPU id to register the current thread as.
|
---|
| 1647 | */
|
---|
[80346] | 1648 | GVMMR0DECL(int) GVMMR0DeregisterVCpu(PGVM pGVM, VMCPUID idCpu)
|
---|
[67987] | 1649 | {
|
---|
| 1650 | AssertReturn(idCpu != 0, VERR_INVALID_FUNCTION);
|
---|
| 1651 |
|
---|
| 1652 | /*
|
---|
| 1653 | * Validate the VM structure, state and handle.
|
---|
| 1654 | */
|
---|
| 1655 | PGVMM pGVMM;
|
---|
[80346] | 1656 | int rc = gvmmR0ByGVMandEMT(pGVM, idCpu, &pGVMM);
|
---|
[67987] | 1657 | if (RT_SUCCESS(rc))
|
---|
| 1658 | {
|
---|
| 1659 | /*
|
---|
| 1660 | * Take the destruction lock and recheck the handle state to
|
---|
| 1661 | * prevent racing GVMMR0DestroyVM.
|
---|
| 1662 | */
|
---|
| 1663 | gvmmR0CreateDestroyLock(pGVMM);
|
---|
[90597] | 1664 |
|
---|
[67987] | 1665 | uint32_t hSelf = pGVM->hSelf;
|
---|
[71152] | 1666 | ASMCompilerBarrier();
|
---|
[67987] | 1667 | if ( hSelf < RT_ELEMENTS(pGVMM->aHandles)
|
---|
| 1668 | && pGVMM->aHandles[hSelf].pvObj != NULL
|
---|
| 1669 | && pGVMM->aHandles[hSelf].pGVM == pGVM)
|
---|
| 1670 | {
|
---|
| 1671 | /*
|
---|
| 1672 | * Do per-EMT cleanups.
|
---|
| 1673 | */
|
---|
[78431] | 1674 | VMMR0ThreadCtxHookDestroyForEmt(&pGVM->aCpus[idCpu]);
|
---|
[67987] | 1675 |
|
---|
| 1676 | /*
|
---|
| 1677 | * Invalidate hEMT. We don't use NIL here as that would allow
|
---|
| 1678 | * GVMMR0RegisterVCpu to be called again, and we don't want that.
|
---|
| 1679 | */
|
---|
[90597] | 1680 | pGVM->aCpus[idCpu].hEMT = GVMM_RTNATIVETHREAD_DESTROYED;
|
---|
[78431] | 1681 | pGVM->aCpus[idCpu].hNativeThreadR0 = NIL_RTNATIVETHREAD;
|
---|
[90597] | 1682 |
|
---|
| 1683 | uint32_t const idxHash = pGVM->aCpus[idCpu].gvmm.s.idxEmtHash;
|
---|
| 1684 | if (idxHash < RT_ELEMENTS(pGVM->gvmm.s.aEmtHash))
|
---|
| 1685 | pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt = GVMM_RTNATIVETHREAD_DESTROYED;
|
---|
[67987] | 1686 | }
|
---|
| 1687 |
|
---|
| 1688 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
| 1689 | }
|
---|
| 1690 | return rc;
|
---|
| 1691 | }
|
---|
| 1692 |
|
---|
| 1693 |
|
---|
| 1694 | /**
|
---|
[92200] | 1695 | * Registers the caller as a given worker thread.
|
---|
| 1696 | *
|
---|
| 1697 | * This enables the thread to operate critical sections in ring-0.
|
---|
| 1698 | *
|
---|
| 1699 | * @returns VBox status code.
|
---|
| 1700 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 1701 | * @param enmWorker The worker thread this is supposed to be.
|
---|
| 1702 | * @param hNativeSelfR3 The ring-3 native self of the caller.
|
---|
| 1703 | */
|
---|
| 1704 | GVMMR0DECL(int) GVMMR0RegisterWorkerThread(PGVM pGVM, GVMMWORKERTHREAD enmWorker, RTNATIVETHREAD hNativeSelfR3)
|
---|
| 1705 | {
|
---|
| 1706 | /*
|
---|
| 1707 | * Validate input.
|
---|
| 1708 | */
|
---|
| 1709 | AssertReturn(enmWorker > GVMMWORKERTHREAD_INVALID && enmWorker < GVMMWORKERTHREAD_END, VERR_INVALID_PARAMETER);
|
---|
| 1710 | AssertReturn(hNativeSelfR3 != NIL_RTNATIVETHREAD, VERR_INVALID_HANDLE);
|
---|
| 1711 | RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
|
---|
| 1712 | AssertReturn(hNativeSelf != NIL_RTNATIVETHREAD, VERR_INTERNAL_ERROR_3);
|
---|
| 1713 | PGVMM pGVMM;
|
---|
| 1714 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, false /*fTakeUsedLock*/);
|
---|
| 1715 | AssertRCReturn(rc, rc);
|
---|
| 1716 | AssertReturn(pGVM->enmVMState < VMSTATE_DESTROYING, VERR_VM_INVALID_VM_STATE);
|
---|
| 1717 |
|
---|
| 1718 | /*
|
---|
| 1719 | * Grab the big lock and check the VM state again.
|
---|
| 1720 | */
|
---|
| 1721 | uint32_t const hSelf = pGVM->hSelf;
|
---|
| 1722 | gvmmR0CreateDestroyLock(pGVMM); /** @todo per-VM lock? */
|
---|
| 1723 | if ( hSelf < RT_ELEMENTS(pGVMM->aHandles)
|
---|
| 1724 | && pGVMM->aHandles[hSelf].pvObj != NULL
|
---|
| 1725 | && pGVMM->aHandles[hSelf].pGVM == pGVM
|
---|
| 1726 | && pGVMM->aHandles[hSelf].ProcId == RTProcSelf())
|
---|
| 1727 | {
|
---|
| 1728 | if (pGVM->enmVMState < VMSTATE_DESTROYING)
|
---|
| 1729 | {
|
---|
| 1730 | /*
|
---|
| 1731 | * Check that the thread isn't an EMT or serving in some other worker capacity.
|
---|
| 1732 | */
|
---|
| 1733 | for (VMCPUID iCpu = 0; iCpu < pGVM->cCpus; iCpu++)
|
---|
| 1734 | AssertBreakStmt(pGVM->aCpus[iCpu].hEMT != hNativeSelf, rc = VERR_INVALID_PARAMETER);
|
---|
| 1735 | for (size_t idx = 0; idx < RT_ELEMENTS(pGVM->gvmm.s.aWorkerThreads); idx++)
|
---|
[92229] | 1736 | AssertBreakStmt(idx == (size_t)enmWorker || pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThread != hNativeSelf,
|
---|
[92200] | 1737 | rc = VERR_INVALID_PARAMETER);
|
---|
| 1738 | if (RT_SUCCESS(rc))
|
---|
| 1739 | {
|
---|
| 1740 | /*
|
---|
| 1741 | * Do the registration.
|
---|
| 1742 | */
|
---|
| 1743 | if ( pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThread == NIL_RTNATIVETHREAD
|
---|
| 1744 | && pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThreadR3 == NIL_RTNATIVETHREAD)
|
---|
| 1745 | {
|
---|
| 1746 | pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThread = hNativeSelf;
|
---|
| 1747 | pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThreadR3 = hNativeSelfR3;
|
---|
| 1748 | rc = VINF_SUCCESS;
|
---|
| 1749 | }
|
---|
| 1750 | else if ( pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThread == hNativeSelf
|
---|
| 1751 | && pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThreadR3 == hNativeSelfR3)
|
---|
| 1752 | rc = VERR_ALREADY_EXISTS;
|
---|
| 1753 | else
|
---|
| 1754 | rc = VERR_RESOURCE_BUSY;
|
---|
| 1755 | }
|
---|
| 1756 | }
|
---|
| 1757 | else
|
---|
| 1758 | rc = VERR_VM_INVALID_VM_STATE;
|
---|
| 1759 | }
|
---|
| 1760 | else
|
---|
| 1761 | rc = VERR_INVALID_VM_HANDLE;
|
---|
| 1762 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
| 1763 | return rc;
|
---|
| 1764 | }
|
---|
| 1765 |
|
---|
| 1766 |
|
---|
[92201] | 1767 | /**
|
---|
[92200] | 1768 | * Deregisters a workinger thread (caller).
|
---|
| 1769 | *
|
---|
| 1770 | * The worker thread cannot be re-created and re-registered, instead the given
|
---|
| 1771 | * @a enmWorker slot becomes invalid.
|
---|
| 1772 | *
|
---|
| 1773 | * @returns VBox status code.
|
---|
| 1774 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 1775 | * @param enmWorker The worker thread this is supposed to be.
|
---|
| 1776 | */
|
---|
| 1777 | GVMMR0DECL(int) GVMMR0DeregisterWorkerThread(PGVM pGVM, GVMMWORKERTHREAD enmWorker)
|
---|
| 1778 | {
|
---|
| 1779 | /*
|
---|
| 1780 | * Validate input.
|
---|
| 1781 | */
|
---|
| 1782 | AssertReturn(enmWorker > GVMMWORKERTHREAD_INVALID && enmWorker < GVMMWORKERTHREAD_END, VERR_INVALID_PARAMETER);
|
---|
| 1783 | RTNATIVETHREAD const hNativeThread = RTThreadNativeSelf();
|
---|
| 1784 | AssertReturn(hNativeThread != NIL_RTNATIVETHREAD, VERR_INTERNAL_ERROR_3);
|
---|
| 1785 | PGVMM pGVMM;
|
---|
| 1786 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, false /*fTakeUsedLock*/);
|
---|
| 1787 | AssertRCReturn(rc, rc);
|
---|
| 1788 |
|
---|
| 1789 | /*
|
---|
| 1790 | * Grab the big lock and check the VM state again.
|
---|
| 1791 | */
|
---|
| 1792 | uint32_t const hSelf = pGVM->hSelf;
|
---|
| 1793 | gvmmR0CreateDestroyLock(pGVMM); /** @todo per-VM lock? */
|
---|
| 1794 | if ( hSelf < RT_ELEMENTS(pGVMM->aHandles)
|
---|
| 1795 | && pGVMM->aHandles[hSelf].pvObj != NULL
|
---|
| 1796 | && pGVMM->aHandles[hSelf].pGVM == pGVM
|
---|
| 1797 | && pGVMM->aHandles[hSelf].ProcId == RTProcSelf())
|
---|
| 1798 | {
|
---|
| 1799 | /*
|
---|
| 1800 | * Do the deregistration.
|
---|
| 1801 | * This will prevent any other threads register as the worker later.
|
---|
| 1802 | */
|
---|
| 1803 | if (pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThread == hNativeThread)
|
---|
| 1804 | {
|
---|
| 1805 | pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThread = GVMM_RTNATIVETHREAD_DESTROYED;
|
---|
| 1806 | pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThreadR3 = GVMM_RTNATIVETHREAD_DESTROYED;
|
---|
| 1807 | rc = VINF_SUCCESS;
|
---|
| 1808 | }
|
---|
| 1809 | else if ( pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThread == GVMM_RTNATIVETHREAD_DESTROYED
|
---|
| 1810 | && pGVM->gvmm.s.aWorkerThreads[enmWorker].hNativeThreadR3 == GVMM_RTNATIVETHREAD_DESTROYED)
|
---|
| 1811 | rc = VINF_SUCCESS;
|
---|
| 1812 | else
|
---|
| 1813 | rc = VERR_NOT_OWNER;
|
---|
| 1814 | }
|
---|
| 1815 | else
|
---|
| 1816 | rc = VERR_INVALID_VM_HANDLE;
|
---|
| 1817 | gvmmR0CreateDestroyUnlock(pGVMM);
|
---|
| 1818 | return rc;
|
---|
| 1819 | }
|
---|
| 1820 |
|
---|
| 1821 |
|
---|
| 1822 | /**
|
---|
[5167] | 1823 | * Lookup a GVM structure by its handle.
|
---|
[5026] | 1824 | *
|
---|
[5167] | 1825 | * @returns The GVM pointer on success, NULL on failure.
|
---|
| 1826 | * @param hGVM The global VM handle. Asserts on bad handle.
|
---|
[5031] | 1827 | */
|
---|
[5167] | 1828 | GVMMR0DECL(PGVM) GVMMR0ByHandle(uint32_t hGVM)
|
---|
[5031] | 1829 | {
|
---|
| 1830 | PGVMM pGVMM;
|
---|
[5167] | 1831 | GVMM_GET_VALID_INSTANCE(pGVMM, NULL);
|
---|
[5031] | 1832 |
|
---|
| 1833 | /*
|
---|
[5167] | 1834 | * Validate.
|
---|
[5031] | 1835 | */
|
---|
[5167] | 1836 | AssertReturn(hGVM != NIL_GVM_HANDLE, NULL);
|
---|
| 1837 | AssertReturn(hGVM < RT_ELEMENTS(pGVMM->aHandles), NULL);
|
---|
[5031] | 1838 |
|
---|
| 1839 | /*
|
---|
[5167] | 1840 | * Look it up.
|
---|
[5031] | 1841 | */
|
---|
[5167] | 1842 | PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
|
---|
| 1843 | AssertPtrReturn(pHandle->pvObj, NULL);
|
---|
| 1844 | PGVM pGVM = pHandle->pGVM;
|
---|
| 1845 | AssertPtrReturn(pGVM, NULL);
|
---|
[5031] | 1846 |
|
---|
[71152] | 1847 | return pGVM;
|
---|
| 1848 | }
|
---|
| 1849 |
|
---|
| 1850 |
|
---|
| 1851 | /**
|
---|
[67955] | 1852 | * Check that the given GVM and VM structures match up.
|
---|
| 1853 | *
|
---|
| 1854 | * The calling thread must be in the same process as the VM. All current lookups
|
---|
| 1855 | * are by threads inside the same process, so this will not be an issue.
|
---|
| 1856 | *
|
---|
| 1857 | * @returns VBox status code.
|
---|
| 1858 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 1859 | * @param ppGVMM Where to store the pointer to the GVMM instance data.
|
---|
| 1860 | * @param fTakeUsedLock Whether to take the used lock or not. We take it in
|
---|
| 1861 | * shared mode when requested.
|
---|
| 1862 | *
|
---|
| 1863 | * Be very careful if not taking the lock as it's
|
---|
| 1864 | * possible that the VM will disappear then!
|
---|
| 1865 | *
|
---|
[80346] | 1866 | * @remark This will not assert on an invalid pGVM but try return silently.
|
---|
[67955] | 1867 | */
|
---|
[80346] | 1868 | static int gvmmR0ByGVM(PGVM pGVM, PGVMM *ppGVMM, bool fTakeUsedLock)
|
---|
[67955] | 1869 | {
|
---|
| 1870 | /*
|
---|
| 1871 | * Check the pointers.
|
---|
| 1872 | */
|
---|
| 1873 | int rc;
|
---|
[80346] | 1874 | if (RT_LIKELY( RT_VALID_PTR(pGVM)
|
---|
[93554] | 1875 | && ((uintptr_t)pGVM & HOST_PAGE_OFFSET_MASK) == 0 ))
|
---|
[67955] | 1876 | {
|
---|
[80346] | 1877 | /*
|
---|
| 1878 | * Get the pGVMM instance and check the VM handle.
|
---|
| 1879 | */
|
---|
| 1880 | PGVMM pGVMM;
|
---|
| 1881 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
| 1882 |
|
---|
| 1883 | uint16_t hGVM = pGVM->hSelf;
|
---|
| 1884 | if (RT_LIKELY( hGVM != NIL_GVM_HANDLE
|
---|
| 1885 | && hGVM < RT_ELEMENTS(pGVMM->aHandles)))
|
---|
[67955] | 1886 | {
|
---|
[80346] | 1887 | RTPROCESS const pidSelf = RTProcSelf();
|
---|
| 1888 | PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
|
---|
| 1889 | if (fTakeUsedLock)
|
---|
[67955] | 1890 | {
|
---|
[80346] | 1891 | rc = GVMMR0_USED_SHARED_LOCK(pGVMM);
|
---|
| 1892 | AssertRCReturn(rc, rc);
|
---|
| 1893 | }
|
---|
| 1894 |
|
---|
| 1895 | if (RT_LIKELY( pHandle->pGVM == pGVM
|
---|
| 1896 | && pHandle->ProcId == pidSelf
|
---|
| 1897 | && RT_VALID_PTR(pHandle->pvObj)))
|
---|
| 1898 | {
|
---|
[67955] | 1899 | /*
|
---|
[80346] | 1900 | * Some more VM data consistency checks.
|
---|
[67955] | 1901 | */
|
---|
[80346] | 1902 | if (RT_LIKELY( pGVM->cCpusUnsafe == pGVM->cCpus
|
---|
| 1903 | && pGVM->hSelfUnsafe == hGVM
|
---|
| 1904 | && pGVM->pSelf == pGVM))
|
---|
[67955] | 1905 | {
|
---|
[80346] | 1906 | if (RT_LIKELY( pGVM->enmVMState >= VMSTATE_CREATING
|
---|
| 1907 | && pGVM->enmVMState <= VMSTATE_TERMINATED))
|
---|
[67955] | 1908 | {
|
---|
[80346] | 1909 | *ppGVMM = pGVMM;
|
---|
| 1910 | return VINF_SUCCESS;
|
---|
[67955] | 1911 | }
|
---|
[80346] | 1912 | rc = VERR_INCONSISTENT_VM_HANDLE;
|
---|
[67955] | 1913 | }
|
---|
[80346] | 1914 | else
|
---|
| 1915 | rc = VERR_INCONSISTENT_VM_HANDLE;
|
---|
[67955] | 1916 | }
|
---|
[80346] | 1917 | else
|
---|
| 1918 | rc = VERR_INVALID_VM_HANDLE;
|
---|
| 1919 |
|
---|
| 1920 | if (fTakeUsedLock)
|
---|
| 1921 | GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
[67955] | 1922 | }
|
---|
| 1923 | else
|
---|
[80346] | 1924 | rc = VERR_INVALID_VM_HANDLE;
|
---|
[67955] | 1925 | }
|
---|
| 1926 | else
|
---|
| 1927 | rc = VERR_INVALID_POINTER;
|
---|
| 1928 | return rc;
|
---|
| 1929 | }
|
---|
| 1930 |
|
---|
| 1931 |
|
---|
| 1932 | /**
|
---|
[80346] | 1933 | * Validates a GVM/VM pair.
|
---|
| 1934 | *
|
---|
| 1935 | * @returns VBox status code.
|
---|
| 1936 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 1937 | */
|
---|
| 1938 | GVMMR0DECL(int) GVMMR0ValidateGVM(PGVM pGVM)
|
---|
| 1939 | {
|
---|
| 1940 | PGVMM pGVMM;
|
---|
| 1941 | return gvmmR0ByGVM(pGVM, &pGVMM, false /*fTakeUsedLock*/);
|
---|
| 1942 | }
|
---|
| 1943 |
|
---|
| 1944 |
|
---|
| 1945 | /**
|
---|
[67987] | 1946 | * Check that the given GVM and VM structures match up.
|
---|
| 1947 | *
|
---|
| 1948 | * The calling thread must be in the same process as the VM. All current lookups
|
---|
| 1949 | * are by threads inside the same process, so this will not be an issue.
|
---|
| 1950 | *
|
---|
| 1951 | * @returns VBox status code.
|
---|
| 1952 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 1953 | * @param idCpu The (alleged) Virtual CPU ID of the calling EMT.
|
---|
| 1954 | * @param ppGVMM Where to store the pointer to the GVMM instance data.
|
---|
| 1955 | * @thread EMT
|
---|
| 1956 | *
|
---|
| 1957 | * @remarks This will assert in all failure paths.
|
---|
| 1958 | */
|
---|
[80346] | 1959 | static int gvmmR0ByGVMandEMT(PGVM pGVM, VMCPUID idCpu, PGVMM *ppGVMM)
|
---|
[67987] | 1960 | {
|
---|
| 1961 | /*
|
---|
| 1962 | * Check the pointers.
|
---|
| 1963 | */
|
---|
| 1964 | AssertPtrReturn(pGVM, VERR_INVALID_POINTER);
|
---|
[93554] | 1965 | AssertReturn(((uintptr_t)pGVM & HOST_PAGE_OFFSET_MASK) == 0, VERR_INVALID_POINTER);
|
---|
[67987] | 1966 |
|
---|
| 1967 | /*
|
---|
| 1968 | * Get the pGVMM instance and check the VM handle.
|
---|
| 1969 | */
|
---|
| 1970 | PGVMM pGVMM;
|
---|
| 1971 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
| 1972 |
|
---|
| 1973 | uint16_t hGVM = pGVM->hSelf;
|
---|
[71152] | 1974 | ASMCompilerBarrier();
|
---|
[67987] | 1975 | AssertReturn( hGVM != NIL_GVM_HANDLE
|
---|
| 1976 | && hGVM < RT_ELEMENTS(pGVMM->aHandles), VERR_INVALID_VM_HANDLE);
|
---|
| 1977 |
|
---|
| 1978 | RTPROCESS const pidSelf = RTProcSelf();
|
---|
| 1979 | PGVMHANDLE pHandle = &pGVMM->aHandles[hGVM];
|
---|
| 1980 | AssertReturn( pHandle->pGVM == pGVM
|
---|
| 1981 | && pHandle->ProcId == pidSelf
|
---|
| 1982 | && RT_VALID_PTR(pHandle->pvObj),
|
---|
| 1983 | VERR_INVALID_HANDLE);
|
---|
| 1984 |
|
---|
| 1985 | /*
|
---|
| 1986 | * Check the EMT claim.
|
---|
| 1987 | */
|
---|
| 1988 | RTNATIVETHREAD const hAllegedEMT = RTThreadNativeSelf();
|
---|
| 1989 | AssertReturn(idCpu < pGVM->cCpus, VERR_INVALID_CPU_ID);
|
---|
| 1990 | AssertReturn(pGVM->aCpus[idCpu].hEMT == hAllegedEMT, VERR_NOT_OWNER);
|
---|
| 1991 |
|
---|
| 1992 | /*
|
---|
| 1993 | * Some more VM data consistency checks.
|
---|
| 1994 | */
|
---|
[80346] | 1995 | AssertReturn(pGVM->cCpusUnsafe == pGVM->cCpus, VERR_INCONSISTENT_VM_HANDLE);
|
---|
| 1996 | AssertReturn(pGVM->hSelfUnsafe == hGVM, VERR_INCONSISTENT_VM_HANDLE);
|
---|
| 1997 | AssertReturn( pGVM->enmVMState >= VMSTATE_CREATING
|
---|
| 1998 | && pGVM->enmVMState <= VMSTATE_TERMINATED, VERR_INCONSISTENT_VM_HANDLE);
|
---|
[67987] | 1999 |
|
---|
| 2000 | *ppGVMM = pGVMM;
|
---|
| 2001 | return VINF_SUCCESS;
|
---|
| 2002 | }
|
---|
| 2003 |
|
---|
[67992] | 2004 |
|
---|
[67987] | 2005 | /**
|
---|
[80346] | 2006 | * Validates a GVM/EMT pair.
|
---|
[68001] | 2007 | *
|
---|
| 2008 | * @returns VBox status code.
|
---|
| 2009 | * @param pGVM The global (ring-0) VM structure.
|
---|
[67992] | 2010 | * @param idCpu The Virtual CPU ID of the calling EMT.
|
---|
| 2011 | * @thread EMT(idCpu)
|
---|
| 2012 | */
|
---|
[80346] | 2013 | GVMMR0DECL(int) GVMMR0ValidateGVMandEMT(PGVM pGVM, VMCPUID idCpu)
|
---|
[67992] | 2014 | {
|
---|
| 2015 | PGVMM pGVMM;
|
---|
[80346] | 2016 | return gvmmR0ByGVMandEMT(pGVM, idCpu, &pGVMM);
|
---|
[67992] | 2017 | }
|
---|
| 2018 |
|
---|
| 2019 |
|
---|
| 2020 | /**
|
---|
[4971] | 2021 | * Looks up the VM belonging to the specified EMT thread.
|
---|
| 2022 | *
|
---|
| 2023 | * This is used by the assertion machinery in VMMR0.cpp to avoid causing
|
---|
| 2024 | * unnecessary kernel panics when the EMT thread hits an assertion. The
|
---|
| 2025 | * call may or not be an EMT thread.
|
---|
| 2026 | *
|
---|
[41777] | 2027 | * @returns Pointer to the VM on success, NULL on failure.
|
---|
[4971] | 2028 | * @param hEMT The native thread handle of the EMT.
|
---|
| 2029 | * NIL_RTNATIVETHREAD means the current thread
|
---|
| 2030 | */
|
---|
[80274] | 2031 | GVMMR0DECL(PVMCC) GVMMR0GetVMByEMT(RTNATIVETHREAD hEMT)
|
---|
[4971] | 2032 | {
|
---|
| 2033 | /*
|
---|
[5031] | 2034 | * No Assertions here as we're usually called in a AssertMsgN or
|
---|
| 2035 | * RTAssert* context.
|
---|
[4971] | 2036 | */
|
---|
[5026] | 2037 | PGVMM pGVMM = g_pGVMM;
|
---|
[80346] | 2038 | if ( !RT_VALID_PTR(pGVMM)
|
---|
[5031] | 2039 | || pGVMM->u32Magic != GVMM_MAGIC)
|
---|
[4971] | 2040 | return NULL;
|
---|
| 2041 |
|
---|
| 2042 | if (hEMT == NIL_RTNATIVETHREAD)
|
---|
| 2043 | hEMT = RTThreadNativeSelf();
|
---|
[19398] | 2044 | RTPROCESS ProcId = RTProcSelf();
|
---|
[4971] | 2045 |
|
---|
| 2046 | /*
|
---|
[19381] | 2047 | * Search the handles in a linear fashion as we don't dare to take the lock (assert).
|
---|
[4971] | 2048 | */
|
---|
[72778] | 2049 | /** @todo introduce some pid hash table here, please. */
|
---|
[5026] | 2050 | for (unsigned i = 1; i < RT_ELEMENTS(pGVMM->aHandles); i++)
|
---|
[19381] | 2051 | {
|
---|
| 2052 | if ( pGVMM->aHandles[i].iSelf == i
|
---|
[19398] | 2053 | && pGVMM->aHandles[i].ProcId == ProcId
|
---|
[80346] | 2054 | && RT_VALID_PTR(pGVMM->aHandles[i].pvObj)
|
---|
| 2055 | && RT_VALID_PTR(pGVMM->aHandles[i].pGVM))
|
---|
[19381] | 2056 | {
|
---|
[19398] | 2057 | if (pGVMM->aHandles[i].hEMT0 == hEMT)
|
---|
[80346] | 2058 | return pGVMM->aHandles[i].pGVM;
|
---|
[19395] | 2059 |
|
---|
[19398] | 2060 | /* This is fearly safe with the current process per VM approach. */
|
---|
[19381] | 2061 | PGVM pGVM = pGVMM->aHandles[i].pGVM;
|
---|
[19398] | 2062 | VMCPUID const cCpus = pGVM->cCpus;
|
---|
[72778] | 2063 | ASMCompilerBarrier();
|
---|
[19398] | 2064 | if ( cCpus < 1
|
---|
[19403] | 2065 | || cCpus > VMM_MAX_CPU_COUNT)
|
---|
[19398] | 2066 | continue;
|
---|
| 2067 | for (VMCPUID idCpu = 1; idCpu < cCpus; idCpu++)
|
---|
[19381] | 2068 | if (pGVM->aCpus[idCpu].hEMT == hEMT)
|
---|
[80346] | 2069 | return pGVMM->aHandles[i].pGVM;
|
---|
[19381] | 2070 | }
|
---|
| 2071 | }
|
---|
[4971] | 2072 | return NULL;
|
---|
| 2073 | }
|
---|
| 2074 |
|
---|
| 2075 |
|
---|
[5167] | 2076 | /**
|
---|
[72778] | 2077 | * Looks up the GVMCPU belonging to the specified EMT thread.
|
---|
| 2078 | *
|
---|
| 2079 | * This is used by the assertion machinery in VMMR0.cpp to avoid causing
|
---|
| 2080 | * unnecessary kernel panics when the EMT thread hits an assertion. The
|
---|
| 2081 | * call may or not be an EMT thread.
|
---|
| 2082 | *
|
---|
| 2083 | * @returns Pointer to the VM on success, NULL on failure.
|
---|
| 2084 | * @param hEMT The native thread handle of the EMT.
|
---|
| 2085 | * NIL_RTNATIVETHREAD means the current thread
|
---|
| 2086 | */
|
---|
| 2087 | GVMMR0DECL(PGVMCPU) GVMMR0GetGVCpuByEMT(RTNATIVETHREAD hEMT)
|
---|
| 2088 | {
|
---|
| 2089 | /*
|
---|
| 2090 | * No Assertions here as we're usually called in a AssertMsgN,
|
---|
| 2091 | * RTAssert*, Log and LogRel contexts.
|
---|
| 2092 | */
|
---|
| 2093 | PGVMM pGVMM = g_pGVMM;
|
---|
[80346] | 2094 | if ( !RT_VALID_PTR(pGVMM)
|
---|
[72778] | 2095 | || pGVMM->u32Magic != GVMM_MAGIC)
|
---|
| 2096 | return NULL;
|
---|
| 2097 |
|
---|
| 2098 | if (hEMT == NIL_RTNATIVETHREAD)
|
---|
| 2099 | hEMT = RTThreadNativeSelf();
|
---|
| 2100 | RTPROCESS ProcId = RTProcSelf();
|
---|
| 2101 |
|
---|
| 2102 | /*
|
---|
| 2103 | * Search the handles in a linear fashion as we don't dare to take the lock (assert).
|
---|
| 2104 | */
|
---|
| 2105 | /** @todo introduce some pid hash table here, please. */
|
---|
| 2106 | for (unsigned i = 1; i < RT_ELEMENTS(pGVMM->aHandles); i++)
|
---|
| 2107 | {
|
---|
| 2108 | if ( pGVMM->aHandles[i].iSelf == i
|
---|
| 2109 | && pGVMM->aHandles[i].ProcId == ProcId
|
---|
[80346] | 2110 | && RT_VALID_PTR(pGVMM->aHandles[i].pvObj)
|
---|
| 2111 | && RT_VALID_PTR(pGVMM->aHandles[i].pGVM))
|
---|
[72778] | 2112 | {
|
---|
| 2113 | PGVM pGVM = pGVMM->aHandles[i].pGVM;
|
---|
| 2114 | if (pGVMM->aHandles[i].hEMT0 == hEMT)
|
---|
| 2115 | return &pGVM->aCpus[0];
|
---|
| 2116 |
|
---|
| 2117 | /* This is fearly safe with the current process per VM approach. */
|
---|
| 2118 | VMCPUID const cCpus = pGVM->cCpus;
|
---|
| 2119 | ASMCompilerBarrier();
|
---|
| 2120 | ASMCompilerBarrier();
|
---|
| 2121 | if ( cCpus < 1
|
---|
| 2122 | || cCpus > VMM_MAX_CPU_COUNT)
|
---|
| 2123 | continue;
|
---|
| 2124 | for (VMCPUID idCpu = 1; idCpu < cCpus; idCpu++)
|
---|
| 2125 | if (pGVM->aCpus[idCpu].hEMT == hEMT)
|
---|
| 2126 | return &pGVM->aCpus[idCpu];
|
---|
| 2127 | }
|
---|
| 2128 | }
|
---|
| 2129 | return NULL;
|
---|
| 2130 | }
|
---|
| 2131 |
|
---|
| 2132 |
|
---|
| 2133 | /**
|
---|
[90597] | 2134 | * Get the GVMCPU structure for the given EMT.
|
---|
| 2135 | *
|
---|
| 2136 | * @returns The VCpu structure for @a hEMT, NULL if not an EMT.
|
---|
| 2137 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 2138 | * @param hEMT The native thread handle of the EMT.
|
---|
| 2139 | * NIL_RTNATIVETHREAD means the current thread
|
---|
| 2140 | */
|
---|
| 2141 | GVMMR0DECL(PGVMCPU) GVMMR0GetGVCpuByGVMandEMT(PGVM pGVM, RTNATIVETHREAD hEMT)
|
---|
| 2142 | {
|
---|
| 2143 | /*
|
---|
| 2144 | * Validate & adjust input.
|
---|
| 2145 | */
|
---|
| 2146 | AssertPtr(pGVM);
|
---|
| 2147 | Assert(pGVM->u32Magic == GVM_MAGIC);
|
---|
| 2148 | if (hEMT == NIL_RTNATIVETHREAD /* likely */)
|
---|
| 2149 | {
|
---|
| 2150 | hEMT = RTThreadNativeSelf();
|
---|
| 2151 | AssertReturn(hEMT != NIL_RTNATIVETHREAD, NULL);
|
---|
| 2152 | }
|
---|
| 2153 |
|
---|
| 2154 | /*
|
---|
| 2155 | * Find the matching hash table entry.
|
---|
[92200] | 2156 | * See similar code in GVMMR0GetRing3ThreadForSelf.
|
---|
[90597] | 2157 | */
|
---|
| 2158 | uint32_t idxHash = GVMM_EMT_HASH_1(hEMT);
|
---|
| 2159 | if (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt == hEMT)
|
---|
| 2160 | { /* likely */ }
|
---|
| 2161 | else
|
---|
| 2162 | {
|
---|
| 2163 | #ifdef VBOX_STRICT
|
---|
| 2164 | unsigned cCollisions = 0;
|
---|
| 2165 | #endif
|
---|
| 2166 | uint32_t const idxHash2 = GVMM_EMT_HASH_2(hEMT);
|
---|
| 2167 | for (;;)
|
---|
| 2168 | {
|
---|
| 2169 | Assert(cCollisions++ < GVMM_EMT_HASH_SIZE);
|
---|
| 2170 | idxHash = (idxHash + idxHash2) % GVMM_EMT_HASH_SIZE;
|
---|
| 2171 | if (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt == hEMT)
|
---|
| 2172 | break;
|
---|
| 2173 | if (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt == NIL_RTNATIVETHREAD)
|
---|
| 2174 | {
|
---|
| 2175 | #ifdef VBOX_STRICT
|
---|
| 2176 | uint32_t idxCpu = pGVM->cCpus;
|
---|
| 2177 | AssertStmt(idxCpu < VMM_MAX_CPU_COUNT, idxCpu = VMM_MAX_CPU_COUNT);
|
---|
| 2178 | while (idxCpu-- > 0)
|
---|
| 2179 | Assert(pGVM->aCpus[idxCpu].hNativeThreadR0 != hEMT);
|
---|
| 2180 | #endif
|
---|
| 2181 | return NULL;
|
---|
| 2182 | }
|
---|
| 2183 | }
|
---|
| 2184 | }
|
---|
| 2185 |
|
---|
| 2186 | /*
|
---|
| 2187 | * Validate the VCpu number and translate it into a pointer.
|
---|
| 2188 | */
|
---|
| 2189 | VMCPUID const idCpu = pGVM->gvmm.s.aEmtHash[idxHash].idVCpu;
|
---|
| 2190 | AssertReturn(idCpu < pGVM->cCpus, NULL);
|
---|
| 2191 | PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
|
---|
| 2192 | Assert(pGVCpu->hNativeThreadR0 == hEMT);
|
---|
| 2193 | Assert(pGVCpu->gvmm.s.idxEmtHash == idxHash);
|
---|
| 2194 | return pGVCpu;
|
---|
| 2195 | }
|
---|
| 2196 |
|
---|
| 2197 |
|
---|
| 2198 | /**
|
---|
[92200] | 2199 | * Get the native ring-3 thread handle for the caller.
|
---|
| 2200 | *
|
---|
| 2201 | * This works for EMTs and registered workers.
|
---|
| 2202 | *
|
---|
| 2203 | * @returns ring-3 native thread handle or NIL_RTNATIVETHREAD.
|
---|
| 2204 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 2205 | */
|
---|
| 2206 | GVMMR0DECL(RTNATIVETHREAD) GVMMR0GetRing3ThreadForSelf(PGVM pGVM)
|
---|
| 2207 | {
|
---|
| 2208 | /*
|
---|
| 2209 | * Validate input.
|
---|
| 2210 | */
|
---|
| 2211 | AssertPtr(pGVM);
|
---|
| 2212 | AssertReturn(pGVM->u32Magic == GVM_MAGIC, NIL_RTNATIVETHREAD);
|
---|
| 2213 | RTNATIVETHREAD const hNativeSelf = RTThreadNativeSelf();
|
---|
| 2214 | AssertReturn(hNativeSelf != NIL_RTNATIVETHREAD, NIL_RTNATIVETHREAD);
|
---|
| 2215 |
|
---|
| 2216 | /*
|
---|
| 2217 | * Find the matching hash table entry.
|
---|
| 2218 | * See similar code in GVMMR0GetGVCpuByGVMandEMT.
|
---|
| 2219 | */
|
---|
| 2220 | uint32_t idxHash = GVMM_EMT_HASH_1(hNativeSelf);
|
---|
| 2221 | if (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt == hNativeSelf)
|
---|
| 2222 | { /* likely */ }
|
---|
| 2223 | else
|
---|
| 2224 | {
|
---|
| 2225 | #ifdef VBOX_STRICT
|
---|
| 2226 | unsigned cCollisions = 0;
|
---|
| 2227 | #endif
|
---|
| 2228 | uint32_t const idxHash2 = GVMM_EMT_HASH_2(hNativeSelf);
|
---|
| 2229 | for (;;)
|
---|
| 2230 | {
|
---|
| 2231 | Assert(cCollisions++ < GVMM_EMT_HASH_SIZE);
|
---|
| 2232 | idxHash = (idxHash + idxHash2) % GVMM_EMT_HASH_SIZE;
|
---|
| 2233 | if (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt == hNativeSelf)
|
---|
| 2234 | break;
|
---|
| 2235 | if (pGVM->gvmm.s.aEmtHash[idxHash].hNativeEmt == NIL_RTNATIVETHREAD)
|
---|
| 2236 | {
|
---|
| 2237 | #ifdef VBOX_STRICT
|
---|
| 2238 | uint32_t idxCpu = pGVM->cCpus;
|
---|
| 2239 | AssertStmt(idxCpu < VMM_MAX_CPU_COUNT, idxCpu = VMM_MAX_CPU_COUNT);
|
---|
| 2240 | while (idxCpu-- > 0)
|
---|
| 2241 | Assert(pGVM->aCpus[idxCpu].hNativeThreadR0 != hNativeSelf);
|
---|
| 2242 | #endif
|
---|
| 2243 |
|
---|
| 2244 | /*
|
---|
| 2245 | * Not an EMT, so see if it's a worker thread.
|
---|
| 2246 | */
|
---|
| 2247 | size_t idx = RT_ELEMENTS(pGVM->gvmm.s.aWorkerThreads);
|
---|
| 2248 | while (--idx > GVMMWORKERTHREAD_INVALID)
|
---|
| 2249 | if (pGVM->gvmm.s.aWorkerThreads[idx].hNativeThread == hNativeSelf)
|
---|
| 2250 | return pGVM->gvmm.s.aWorkerThreads[idx].hNativeThreadR3;
|
---|
| 2251 |
|
---|
| 2252 | return NIL_RTNATIVETHREAD;
|
---|
| 2253 | }
|
---|
| 2254 | }
|
---|
| 2255 | }
|
---|
| 2256 |
|
---|
| 2257 | /*
|
---|
| 2258 | * Validate the VCpu number and translate it into a pointer.
|
---|
| 2259 | */
|
---|
| 2260 | VMCPUID const idCpu = pGVM->gvmm.s.aEmtHash[idxHash].idVCpu;
|
---|
| 2261 | AssertReturn(idCpu < pGVM->cCpus, NIL_RTNATIVETHREAD);
|
---|
| 2262 | PGVMCPU pGVCpu = &pGVM->aCpus[idCpu];
|
---|
| 2263 | Assert(pGVCpu->hNativeThreadR0 == hNativeSelf);
|
---|
| 2264 | Assert(pGVCpu->gvmm.s.idxEmtHash == idxHash);
|
---|
| 2265 | return pGVCpu->hNativeThread;
|
---|
| 2266 | }
|
---|
| 2267 |
|
---|
| 2268 |
|
---|
| 2269 | /**
|
---|
[91287] | 2270 | * Converts a pointer with the GVM structure to a host physical address.
|
---|
| 2271 | *
|
---|
| 2272 | * @returns Host physical address.
|
---|
| 2273 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 2274 | * @param pv The address to convert.
|
---|
| 2275 | * @thread EMT
|
---|
| 2276 | */
|
---|
| 2277 | GVMMR0DECL(RTHCPHYS) GVMMR0ConvertGVMPtr2HCPhys(PGVM pGVM, void *pv)
|
---|
| 2278 | {
|
---|
| 2279 | AssertPtr(pGVM);
|
---|
| 2280 | Assert(pGVM->u32Magic == GVM_MAGIC);
|
---|
| 2281 | uintptr_t const off = (uintptr_t)pv - (uintptr_t)pGVM;
|
---|
| 2282 | Assert(off < RT_UOFFSETOF_DYN(GVM, aCpus[pGVM->cCpus]));
|
---|
[93554] | 2283 | return RTR0MemObjGetPagePhysAddr(pGVM->gvmm.s.VMMemObj, off >> HOST_PAGE_SHIFT) | ((uintptr_t)pv & HOST_PAGE_OFFSET_MASK);
|
---|
[91287] | 2284 | }
|
---|
| 2285 |
|
---|
| 2286 |
|
---|
| 2287 | /**
|
---|
[5167] | 2288 | * This is will wake up expired and soon-to-be expired VMs.
|
---|
| 2289 | *
|
---|
| 2290 | * @returns Number of VMs that has been woken up.
|
---|
| 2291 | * @param pGVMM Pointer to the GVMM instance data.
|
---|
| 2292 | * @param u64Now The current time.
|
---|
| 2293 | */
|
---|
| 2294 | static unsigned gvmmR0SchedDoWakeUps(PGVMM pGVMM, uint64_t u64Now)
|
---|
| 2295 | {
|
---|
[33170] | 2296 | /*
|
---|
| 2297 | * Skip this if we've got disabled because of high resolution wakeups or by
|
---|
| 2298 | * the user.
|
---|
| 2299 | */
|
---|
[58830] | 2300 | if (!pGVMM->fDoEarlyWakeUps)
|
---|
[33170] | 2301 | return 0;
|
---|
| 2302 |
|
---|
[19395] | 2303 | /** @todo Rewrite this algorithm. See performance defect XYZ. */
|
---|
| 2304 |
|
---|
[5167] | 2305 | /*
|
---|
[33170] | 2306 | * A cheap optimization to stop wasting so much time here on big setups.
|
---|
| 2307 | */
|
---|
| 2308 | const uint64_t uNsEarlyWakeUp2 = u64Now + pGVMM->nsEarlyWakeUp2;
|
---|
| 2309 | if ( pGVMM->cHaltedEMTs == 0
|
---|
| 2310 | || uNsEarlyWakeUp2 > pGVMM->uNsNextEmtWakeup)
|
---|
| 2311 | return 0;
|
---|
| 2312 |
|
---|
| 2313 | /*
|
---|
[58830] | 2314 | * Only one thread doing this at a time.
|
---|
| 2315 | */
|
---|
| 2316 | if (!ASMAtomicCmpXchgBool(&pGVMM->fDoingEarlyWakeUps, true, false))
|
---|
| 2317 | return 0;
|
---|
| 2318 |
|
---|
| 2319 | /*
|
---|
[16575] | 2320 | * The first pass will wake up VMs which have actually expired
|
---|
[5167] | 2321 | * and look for VMs that should be woken up in the 2nd and 3rd passes.
|
---|
| 2322 | */
|
---|
[33170] | 2323 | const uint64_t uNsEarlyWakeUp1 = u64Now + pGVMM->nsEarlyWakeUp1;
|
---|
| 2324 | uint64_t u64Min = UINT64_MAX;
|
---|
| 2325 | unsigned cWoken = 0;
|
---|
| 2326 | unsigned cHalted = 0;
|
---|
| 2327 | unsigned cTodo2nd = 0;
|
---|
| 2328 | unsigned cTodo3rd = 0;
|
---|
[7018] | 2329 | for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
|
---|
[5167] | 2330 | i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
|
---|
| 2331 | i = pGVMM->aHandles[i].iNext)
|
---|
| 2332 | {
|
---|
| 2333 | PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
|
---|
[80346] | 2334 | if ( RT_VALID_PTR(pCurGVM)
|
---|
[5167] | 2335 | && pCurGVM->u32Magic == GVM_MAGIC)
|
---|
| 2336 | {
|
---|
[19395] | 2337 | for (VMCPUID idCpu = 0; idCpu < pCurGVM->cCpus; idCpu++)
|
---|
[5167] | 2338 | {
|
---|
[33170] | 2339 | PGVMCPU pCurGVCpu = &pCurGVM->aCpus[idCpu];
|
---|
| 2340 | uint64_t u64 = ASMAtomicUoReadU64(&pCurGVCpu->gvmm.s.u64HaltExpire);
|
---|
[19360] | 2341 | if (u64)
|
---|
[5167] | 2342 | {
|
---|
[19360] | 2343 | if (u64 <= u64Now)
|
---|
[5240] | 2344 | {
|
---|
[19360] | 2345 | if (ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, 0))
|
---|
| 2346 | {
|
---|
| 2347 | int rc = RTSemEventMultiSignal(pCurGVCpu->gvmm.s.HaltEventMulti);
|
---|
| 2348 | AssertRC(rc);
|
---|
| 2349 | cWoken++;
|
---|
| 2350 | }
|
---|
[5240] | 2351 | }
|
---|
[19360] | 2352 | else
|
---|
| 2353 | {
|
---|
| 2354 | cHalted++;
|
---|
[33170] | 2355 | if (u64 <= uNsEarlyWakeUp1)
|
---|
[19360] | 2356 | cTodo2nd++;
|
---|
[33170] | 2357 | else if (u64 <= uNsEarlyWakeUp2)
|
---|
[19360] | 2358 | cTodo3rd++;
|
---|
[33170] | 2359 | else if (u64 < u64Min)
|
---|
| 2360 | u64 = u64Min;
|
---|
[19360] | 2361 | }
|
---|
[5167] | 2362 | }
|
---|
| 2363 | }
|
---|
| 2364 | }
|
---|
[7018] | 2365 | AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
|
---|
[5167] | 2366 | }
|
---|
| 2367 |
|
---|
| 2368 | if (cTodo2nd)
|
---|
| 2369 | {
|
---|
[7135] | 2370 | for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
|
---|
[5167] | 2371 | i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
|
---|
| 2372 | i = pGVMM->aHandles[i].iNext)
|
---|
| 2373 | {
|
---|
| 2374 | PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
|
---|
[80346] | 2375 | if ( RT_VALID_PTR(pCurGVM)
|
---|
[19360] | 2376 | && pCurGVM->u32Magic == GVM_MAGIC)
|
---|
[5167] | 2377 | {
|
---|
[19395] | 2378 | for (VMCPUID idCpu = 0; idCpu < pCurGVM->cCpus; idCpu++)
|
---|
[5240] | 2379 | {
|
---|
[33170] | 2380 | PGVMCPU pCurGVCpu = &pCurGVM->aCpus[idCpu];
|
---|
| 2381 | uint64_t u64 = ASMAtomicUoReadU64(&pCurGVCpu->gvmm.s.u64HaltExpire);
|
---|
| 2382 | if ( u64
|
---|
| 2383 | && u64 <= uNsEarlyWakeUp1)
|
---|
[19360] | 2384 | {
|
---|
| 2385 | if (ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, 0))
|
---|
| 2386 | {
|
---|
| 2387 | int rc = RTSemEventMultiSignal(pCurGVCpu->gvmm.s.HaltEventMulti);
|
---|
| 2388 | AssertRC(rc);
|
---|
| 2389 | cWoken++;
|
---|
| 2390 | }
|
---|
| 2391 | }
|
---|
[5240] | 2392 | }
|
---|
[5167] | 2393 | }
|
---|
[7018] | 2394 | AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
|
---|
[5167] | 2395 | }
|
---|
| 2396 | }
|
---|
| 2397 |
|
---|
| 2398 | if (cTodo3rd)
|
---|
| 2399 | {
|
---|
[7018] | 2400 | for (unsigned i = pGVMM->iUsedHead, cGuard = 0;
|
---|
[5167] | 2401 | i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
|
---|
| 2402 | i = pGVMM->aHandles[i].iNext)
|
---|
| 2403 | {
|
---|
| 2404 | PGVM pCurGVM = pGVMM->aHandles[i].pGVM;
|
---|
[80346] | 2405 | if ( RT_VALID_PTR(pCurGVM)
|
---|
[19360] | 2406 | && pCurGVM->u32Magic == GVM_MAGIC)
|
---|
[5167] | 2407 | {
|
---|
[19395] | 2408 | for (VMCPUID idCpu = 0; idCpu < pCurGVM->cCpus; idCpu++)
|
---|
[5240] | 2409 | {
|
---|
[33170] | 2410 | PGVMCPU pCurGVCpu = &pCurGVM->aCpus[idCpu];
|
---|
| 2411 | uint64_t u64 = ASMAtomicUoReadU64(&pCurGVCpu->gvmm.s.u64HaltExpire);
|
---|
| 2412 | if ( u64
|
---|
| 2413 | && u64 <= uNsEarlyWakeUp2)
|
---|
[19360] | 2414 | {
|
---|
| 2415 | if (ASMAtomicXchgU64(&pCurGVCpu->gvmm.s.u64HaltExpire, 0))
|
---|
| 2416 | {
|
---|
| 2417 | int rc = RTSemEventMultiSignal(pCurGVCpu->gvmm.s.HaltEventMulti);
|
---|
| 2418 | AssertRC(rc);
|
---|
| 2419 | cWoken++;
|
---|
| 2420 | }
|
---|
| 2421 | }
|
---|
[5240] | 2422 | }
|
---|
[5167] | 2423 | }
|
---|
[7018] | 2424 | AssertLogRelBreak(cGuard++ < RT_ELEMENTS(pGVMM->aHandles));
|
---|
[5167] | 2425 | }
|
---|
| 2426 | }
|
---|
| 2427 |
|
---|
[33170] | 2428 | /*
|
---|
| 2429 | * Set the minimum value.
|
---|
| 2430 | */
|
---|
| 2431 | pGVMM->uNsNextEmtWakeup = u64Min;
|
---|
| 2432 |
|
---|
[58830] | 2433 | ASMAtomicWriteBool(&pGVMM->fDoingEarlyWakeUps, false);
|
---|
[5167] | 2434 | return cWoken;
|
---|
| 2435 | }
|
---|
| 2436 |
|
---|
| 2437 |
|
---|
[92892] | 2438 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
[5167] | 2439 | /**
|
---|
[92892] | 2440 | * Timer callback for the EMT high-resolution wake-up timer.
|
---|
| 2441 | *
|
---|
| 2442 | * @param pTimer The timer handle.
|
---|
| 2443 | * @param pvUser The global (ring-0) CPU structure for the EMT to wake up.
|
---|
| 2444 | * @param iTick The current tick.
|
---|
| 2445 | */
|
---|
| 2446 | static DECLCALLBACK(void) gvmmR0EmtWakeUpTimerCallback(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
|
---|
| 2447 | {
|
---|
| 2448 | PGVMCPU pGVCpu = (PGVMCPU)pvUser;
|
---|
| 2449 | NOREF(pTimer); NOREF(iTick);
|
---|
| 2450 |
|
---|
| 2451 | pGVCpu->gvmm.s.fHrWakeUptimerArmed = false;
|
---|
| 2452 | if (pGVCpu->gvmm.s.u64HaltExpire != 0)
|
---|
| 2453 | {
|
---|
| 2454 | RTSemEventMultiSignal(pGVCpu->gvmm.s.HaltEventMulti);
|
---|
| 2455 | pGVCpu->gvmm.s.Stats.cWakeUpTimerHits += 1;
|
---|
| 2456 | }
|
---|
| 2457 | else
|
---|
| 2458 | pGVCpu->gvmm.s.Stats.cWakeUpTimerMisses += 1;
|
---|
| 2459 |
|
---|
| 2460 | if (RTMpCpuId() == pGVCpu->gvmm.s.idHaltedOnCpu)
|
---|
| 2461 | pGVCpu->gvmm.s.Stats.cWakeUpTimerSameCpu += 1;
|
---|
| 2462 | }
|
---|
| 2463 | #endif /* GVMM_SCHED_WITH_HR_WAKE_UP_TIMER */
|
---|
| 2464 |
|
---|
| 2465 |
|
---|
| 2466 | /**
|
---|
[5167] | 2467 | * Halt the EMT thread.
|
---|
| 2468 | *
|
---|
| 2469 | * @returns VINF_SUCCESS normal wakeup (timeout or kicked by other thread).
|
---|
| 2470 | * VERR_INTERRUPTED if a signal was scheduled for the thread.
|
---|
[67989] | 2471 | * @param pGVM The global (ring-0) VM structure.
|
---|
[75661] | 2472 | * @param pGVCpu The global (ring-0) CPU structure of the calling
|
---|
| 2473 | * EMT.
|
---|
[5167] | 2474 | * @param u64ExpireGipTime The time for the sleep to expire expressed as GIP time.
|
---|
[75661] | 2475 | * @thread EMT(pGVCpu).
|
---|
[5167] | 2476 | */
|
---|
[80346] | 2477 | GVMMR0DECL(int) GVMMR0SchedHalt(PGVM pGVM, PGVMCPU pGVCpu, uint64_t u64ExpireGipTime)
|
---|
[5167] | 2478 | {
|
---|
[80346] | 2479 | LogFlow(("GVMMR0SchedHalt: pGVM=%p pGVCpu=%p(%d) u64ExpireGipTime=%#RX64\n",
|
---|
| 2480 | pGVM, pGVCpu, pGVCpu->idCpu, u64ExpireGipTime));
|
---|
[19395] | 2481 | PGVMM pGVMM;
|
---|
[75646] | 2482 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
| 2483 |
|
---|
[5211] | 2484 | pGVM->gvmm.s.StatsSched.cHaltCalls++;
|
---|
[75661] | 2485 | Assert(!pGVCpu->gvmm.s.u64HaltExpire);
|
---|
[5167] | 2486 |
|
---|
| 2487 | /*
|
---|
[58830] | 2488 | * If we're doing early wake-ups, we must take the UsedList lock before we
|
---|
| 2489 | * start querying the current time.
|
---|
| 2490 | * Note! Interrupts must NOT be disabled at this point because we ask for GIP time!
|
---|
[5167] | 2491 | */
|
---|
[58830] | 2492 | bool const fDoEarlyWakeUps = pGVMM->fDoEarlyWakeUps;
|
---|
| 2493 | if (fDoEarlyWakeUps)
|
---|
| 2494 | {
|
---|
[75646] | 2495 | int rc2 = GVMMR0_USED_SHARED_LOCK(pGVMM); AssertRC(rc2);
|
---|
[58830] | 2496 | }
|
---|
[5167] | 2497 |
|
---|
[33170] | 2498 | /* GIP hack: We might are frequently sleeping for short intervals where the
|
---|
| 2499 | difference between GIP and system time matters on systems with high resolution
|
---|
| 2500 | system time. So, convert the input from GIP to System time in that case. */
|
---|
[5167] | 2501 | Assert(ASMGetFlags() & X86_EFL_IF);
|
---|
[33170] | 2502 | const uint64_t u64NowSys = RTTimeSystemNanoTS();
|
---|
| 2503 | const uint64_t u64NowGip = RTTimeNanoTS();
|
---|
[5167] | 2504 |
|
---|
[58830] | 2505 | if (fDoEarlyWakeUps)
|
---|
| 2506 | pGVM->gvmm.s.StatsSched.cHaltWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64NowGip);
|
---|
| 2507 |
|
---|
[5167] | 2508 | /*
|
---|
| 2509 | * Go to sleep if we must...
|
---|
[33170] | 2510 | * Cap the sleep time to 1 second to be on the safe side.
|
---|
[5167] | 2511 | */
|
---|
[75646] | 2512 | int rc;
|
---|
[33170] | 2513 | uint64_t cNsInterval = u64ExpireGipTime - u64NowGip;
|
---|
| 2514 | if ( u64NowGip < u64ExpireGipTime
|
---|
[92892] | 2515 | && ( cNsInterval >= (pGVMM->cEMTs > pGVMM->cEMTsMeansCompany
|
---|
| 2516 | ? pGVMM->nsMinSleepCompany
|
---|
| 2517 | : pGVMM->nsMinSleepAlone)
|
---|
| 2518 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 2519 | || (pGVCpu->gvmm.s.hHrWakeUpTimer != NULL && cNsInterval >= pGVMM->nsMinSleepWithHrTimer)
|
---|
| 2520 | #endif
|
---|
| 2521 | )
|
---|
| 2522 | )
|
---|
[5167] | 2523 | {
|
---|
[5211] | 2524 | pGVM->gvmm.s.StatsSched.cHaltBlocking++;
|
---|
[33170] | 2525 | if (cNsInterval > RT_NS_1SEC)
|
---|
| 2526 | u64ExpireGipTime = u64NowGip + RT_NS_1SEC;
|
---|
[75661] | 2527 | ASMAtomicWriteU64(&pGVCpu->gvmm.s.u64HaltExpire, u64ExpireGipTime);
|
---|
[33170] | 2528 | ASMAtomicIncU32(&pGVMM->cHaltedEMTs);
|
---|
[58830] | 2529 | if (fDoEarlyWakeUps)
|
---|
| 2530 | {
|
---|
| 2531 | if (u64ExpireGipTime < pGVMM->uNsNextEmtWakeup)
|
---|
| 2532 | pGVMM->uNsNextEmtWakeup = u64ExpireGipTime;
|
---|
| 2533 | GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
| 2534 | }
|
---|
[5167] | 2535 |
|
---|
[92892] | 2536 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 2537 | if ( pGVCpu->gvmm.s.hHrWakeUpTimer != NULL
|
---|
| 2538 | && cNsInterval >= RT_MIN(RT_NS_1US, pGVMM->nsMinSleepWithHrTimer))
|
---|
| 2539 | {
|
---|
| 2540 | STAM_REL_PROFILE_START(&pGVCpu->gvmm.s.Stats.Start, a);
|
---|
| 2541 | RTTimerStart(pGVCpu->gvmm.s.hHrWakeUpTimer, cNsInterval);
|
---|
| 2542 | pGVCpu->gvmm.s.fHrWakeUptimerArmed = true;
|
---|
| 2543 | pGVCpu->gvmm.s.idHaltedOnCpu = RTMpCpuId();
|
---|
| 2544 | STAM_REL_PROFILE_STOP(&pGVCpu->gvmm.s.Stats.Start, a);
|
---|
| 2545 | }
|
---|
| 2546 | #endif
|
---|
| 2547 |
|
---|
[75661] | 2548 | rc = RTSemEventMultiWaitEx(pGVCpu->gvmm.s.HaltEventMulti,
|
---|
[33170] | 2549 | RTSEMWAIT_FLAGS_ABSOLUTE | RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_INTERRUPTIBLE,
|
---|
| 2550 | u64NowGip > u64NowSys ? u64ExpireGipTime : u64NowSys + cNsInterval);
|
---|
| 2551 |
|
---|
[75661] | 2552 | ASMAtomicWriteU64(&pGVCpu->gvmm.s.u64HaltExpire, 0);
|
---|
[33170] | 2553 | ASMAtomicDecU32(&pGVMM->cHaltedEMTs);
|
---|
| 2554 |
|
---|
[92892] | 2555 | #ifdef GVMM_SCHED_WITH_HR_WAKE_UP_TIMER
|
---|
| 2556 | if (!pGVCpu->gvmm.s.fHrWakeUptimerArmed)
|
---|
| 2557 | { /* likely */ }
|
---|
| 2558 | else
|
---|
| 2559 | {
|
---|
| 2560 | STAM_REL_PROFILE_START(&pGVCpu->gvmm.s.Stats.Stop, a);
|
---|
| 2561 | RTTimerStop(pGVCpu->gvmm.s.hHrWakeUpTimer);
|
---|
| 2562 | pGVCpu->gvmm.s.fHrWakeUptimerArmed = false;
|
---|
| 2563 | pGVCpu->gvmm.s.Stats.cWakeUpTimerCanceled += 1;
|
---|
| 2564 | STAM_REL_PROFILE_STOP(&pGVCpu->gvmm.s.Stats.Stop, a);
|
---|
| 2565 | }
|
---|
| 2566 | #endif
|
---|
| 2567 |
|
---|
[33170] | 2568 | /* Reset the semaphore to try prevent a few false wake-ups. */
|
---|
| 2569 | if (rc == VINF_SUCCESS)
|
---|
[75661] | 2570 | RTSemEventMultiReset(pGVCpu->gvmm.s.HaltEventMulti);
|
---|
[33170] | 2571 | else if (rc == VERR_TIMEOUT)
|
---|
[5211] | 2572 | {
|
---|
| 2573 | pGVM->gvmm.s.StatsSched.cHaltTimeouts++;
|
---|
[5167] | 2574 | rc = VINF_SUCCESS;
|
---|
[5211] | 2575 | }
|
---|
[5167] | 2576 | }
|
---|
| 2577 | else
|
---|
[5211] | 2578 | {
|
---|
| 2579 | pGVM->gvmm.s.StatsSched.cHaltNotBlocking++;
|
---|
[58830] | 2580 | if (fDoEarlyWakeUps)
|
---|
| 2581 | GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
[75661] | 2582 | RTSemEventMultiReset(pGVCpu->gvmm.s.HaltEventMulti);
|
---|
[75646] | 2583 | rc = VINF_SUCCESS;
|
---|
[5211] | 2584 | }
|
---|
[5167] | 2585 |
|
---|
| 2586 | return rc;
|
---|
| 2587 | }
|
---|
| 2588 |
|
---|
| 2589 |
|
---|
| 2590 | /**
|
---|
[75646] | 2591 | * Halt the EMT thread.
|
---|
| 2592 | *
|
---|
| 2593 | * @returns VINF_SUCCESS normal wakeup (timeout or kicked by other thread).
|
---|
| 2594 | * VERR_INTERRUPTED if a signal was scheduled for the thread.
|
---|
| 2595 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 2596 | * @param idCpu The Virtual CPU ID of the calling EMT.
|
---|
| 2597 | * @param u64ExpireGipTime The time for the sleep to expire expressed as GIP time.
|
---|
| 2598 | * @thread EMT(idCpu).
|
---|
| 2599 | */
|
---|
[80346] | 2600 | GVMMR0DECL(int) GVMMR0SchedHaltReq(PGVM pGVM, VMCPUID idCpu, uint64_t u64ExpireGipTime)
|
---|
[75646] | 2601 | {
|
---|
| 2602 | PGVMM pGVMM;
|
---|
[80346] | 2603 | int rc = gvmmR0ByGVMandEMT(pGVM, idCpu, &pGVMM);
|
---|
[75646] | 2604 | if (RT_SUCCESS(rc))
|
---|
[80346] | 2605 | rc = GVMMR0SchedHalt(pGVM, &pGVM->aCpus[idCpu], u64ExpireGipTime);
|
---|
[75646] | 2606 | return rc;
|
---|
| 2607 | }
|
---|
| 2608 |
|
---|
| 2609 |
|
---|
| 2610 |
|
---|
| 2611 | /**
|
---|
[19406] | 2612 | * Worker for GVMMR0SchedWakeUp and GVMMR0SchedWakeUpAndPokeCpus that wakes up
|
---|
| 2613 | * the a sleeping EMT.
|
---|
| 2614 | *
|
---|
| 2615 | * @retval VINF_SUCCESS if successfully woken up.
|
---|
| 2616 | * @retval VINF_GVM_NOT_BLOCKED if the EMT wasn't blocked.
|
---|
| 2617 | *
|
---|
| 2618 | * @param pGVM The global (ring-0) VM structure.
|
---|
| 2619 | * @param pGVCpu The global (ring-0) VCPU structure.
|
---|
| 2620 | */
|
---|
| 2621 | DECLINLINE(int) gvmmR0SchedWakeUpOne(PGVM pGVM, PGVMCPU pGVCpu)
|
---|
| 2622 | {
|
---|
| 2623 | pGVM->gvmm.s.StatsSched.cWakeUpCalls++;
|
---|
| 2624 |
|
---|
| 2625 | /*
|
---|
| 2626 | * Signal the semaphore regardless of whether it's current blocked on it.
|
---|
| 2627 | *
|
---|
| 2628 | * The reason for this is that there is absolutely no way we can be 100%
|
---|
| 2629 | * certain that it isn't *about* go to go to sleep on it and just got
|
---|
| 2630 | * delayed a bit en route. So, we will always signal the semaphore when
|
---|
| 2631 | * the it is flagged as halted in the VMM.
|
---|
| 2632 | */
|
---|
| 2633 | /** @todo we can optimize some of that by means of the pVCpu->enmState now. */
|
---|
[19454] | 2634 | int rc;
|
---|
[19406] | 2635 | if (pGVCpu->gvmm.s.u64HaltExpire)
|
---|
| 2636 | {
|
---|
| 2637 | rc = VINF_SUCCESS;
|
---|
[33170] | 2638 | ASMAtomicWriteU64(&pGVCpu->gvmm.s.u64HaltExpire, 0);
|
---|
[19406] | 2639 | }
|
---|
| 2640 | else
|
---|
| 2641 | {
|
---|
| 2642 | rc = VINF_GVM_NOT_BLOCKED;
|
---|
| 2643 | pGVM->gvmm.s.StatsSched.cWakeUpNotHalted++;
|
---|
| 2644 | }
|
---|
| 2645 |
|
---|
| 2646 | int rc2 = RTSemEventMultiSignal(pGVCpu->gvmm.s.HaltEventMulti);
|
---|
| 2647 | AssertRC(rc2);
|
---|
| 2648 |
|
---|
| 2649 | return rc;
|
---|
| 2650 | }
|
---|
| 2651 |
|
---|
| 2652 |
|
---|
| 2653 | /**
|
---|
[5167] | 2654 | * Wakes up the halted EMT thread so it can service a pending request.
|
---|
| 2655 | *
|
---|
[19406] | 2656 | * @returns VBox status code.
|
---|
| 2657 | * @retval VINF_SUCCESS if successfully woken up.
|
---|
| 2658 | * @retval VINF_GVM_NOT_BLOCKED if the EMT wasn't blocked.
|
---|
| 2659 | *
|
---|
[67989] | 2660 | * @param pGVM The global (ring-0) VM structure.
|
---|
[19395] | 2661 | * @param idCpu The Virtual CPU ID of the EMT to wake up.
|
---|
[19652] | 2662 | * @param fTakeUsedLock Take the used lock or not
|
---|
[67989] | 2663 | * @thread Any but EMT(idCpu).
|
---|
[5167] | 2664 | */
|
---|
[80346] | 2665 | GVMMR0DECL(int) GVMMR0SchedWakeUpEx(PGVM pGVM, VMCPUID idCpu, bool fTakeUsedLock)
|
---|
[5167] | 2666 | {
|
---|
| 2667 | /*
|
---|
| 2668 | * Validate input and take the UsedLock.
|
---|
| 2669 | */
|
---|
[19395] | 2670 | PGVMM pGVMM;
|
---|
[80346] | 2671 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, fTakeUsedLock);
|
---|
[5167] | 2672 | if (RT_SUCCESS(rc))
|
---|
| 2673 | {
|
---|
[19395] | 2674 | if (idCpu < pGVM->cCpus)
|
---|
| 2675 | {
|
---|
| 2676 | /*
|
---|
[19406] | 2677 | * Do the actual job.
|
---|
[19395] | 2678 | */
|
---|
[19406] | 2679 | rc = gvmmR0SchedWakeUpOne(pGVM, &pGVM->aCpus[idCpu]);
|
---|
[19360] | 2680 |
|
---|
[58830] | 2681 | if (fTakeUsedLock && pGVMM->fDoEarlyWakeUps)
|
---|
[19657] | 2682 | {
|
---|
| 2683 | /*
|
---|
| 2684 | * While we're here, do a round of scheduling.
|
---|
| 2685 | */
|
---|
| 2686 | Assert(ASMGetFlags() & X86_EFL_IF);
|
---|
| 2687 | const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
|
---|
| 2688 | pGVM->gvmm.s.StatsSched.cWakeUpWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
|
---|
| 2689 | }
|
---|
[5211] | 2690 | }
|
---|
| 2691 | else
|
---|
[19395] | 2692 | rc = VERR_INVALID_CPU_ID;
|
---|
[5167] | 2693 |
|
---|
[19657] | 2694 | if (fTakeUsedLock)
|
---|
| 2695 | {
|
---|
[58830] | 2696 | int rc2 = GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
[19657] | 2697 | AssertRC(rc2);
|
---|
| 2698 | }
|
---|
[5167] | 2699 | }
|
---|
| 2700 |
|
---|
[67989] | 2701 | LogFlow(("GVMMR0SchedWakeUpEx: returns %Rrc\n", rc));
|
---|
[5167] | 2702 | return rc;
|
---|
| 2703 | }
|
---|
| 2704 |
|
---|
[19660] | 2705 |
|
---|
[19652] | 2706 | /**
|
---|
| 2707 | * Wakes up the halted EMT thread so it can service a pending request.
|
---|
| 2708 | *
|
---|
| 2709 | * @returns VBox status code.
|
---|
| 2710 | * @retval VINF_SUCCESS if successfully woken up.
|
---|
| 2711 | * @retval VINF_GVM_NOT_BLOCKED if the EMT wasn't blocked.
|
---|
| 2712 | *
|
---|
[67989] | 2713 | * @param pGVM The global (ring-0) VM structure.
|
---|
[19652] | 2714 | * @param idCpu The Virtual CPU ID of the EMT to wake up.
|
---|
[67989] | 2715 | * @thread Any but EMT(idCpu).
|
---|
[19652] | 2716 | */
|
---|
[80346] | 2717 | GVMMR0DECL(int) GVMMR0SchedWakeUp(PGVM pGVM, VMCPUID idCpu)
|
---|
[19652] | 2718 | {
|
---|
[80346] | 2719 | return GVMMR0SchedWakeUpEx(pGVM, idCpu, true /* fTakeUsedLock */);
|
---|
[19652] | 2720 | }
|
---|
[5167] | 2721 |
|
---|
[67989] | 2722 |
|
---|
[5167] | 2723 | /**
|
---|
[67989] | 2724 | * Wakes up the halted EMT thread so it can service a pending request, no GVM
|
---|
| 2725 | * parameter and no used locking.
|
---|
| 2726 | *
|
---|
| 2727 | * @returns VBox status code.
|
---|
| 2728 | * @retval VINF_SUCCESS if successfully woken up.
|
---|
| 2729 | * @retval VINF_GVM_NOT_BLOCKED if the EMT wasn't blocked.
|
---|
| 2730 | *
|
---|
[80346] | 2731 | * @param pGVM The global (ring-0) VM structure.
|
---|
[67989] | 2732 | * @param idCpu The Virtual CPU ID of the EMT to wake up.
|
---|
| 2733 | * @thread Any but EMT(idCpu).
|
---|
| 2734 | * @deprecated Don't use in new code if possible! Use the GVM variant.
|
---|
| 2735 | */
|
---|
[80346] | 2736 | GVMMR0DECL(int) GVMMR0SchedWakeUpNoGVMNoLock(PGVM pGVM, VMCPUID idCpu)
|
---|
[67989] | 2737 | {
|
---|
| 2738 | PGVMM pGVMM;
|
---|
[80346] | 2739 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, false /*fTakeUsedLock*/);
|
---|
[67989] | 2740 | if (RT_SUCCESS(rc))
|
---|
[80346] | 2741 | rc = GVMMR0SchedWakeUpEx(pGVM, idCpu, false /*fTakeUsedLock*/);
|
---|
[67989] | 2742 | return rc;
|
---|
| 2743 | }
|
---|
| 2744 |
|
---|
| 2745 |
|
---|
| 2746 | /**
|
---|
[19406] | 2747 | * Worker common to GVMMR0SchedPoke and GVMMR0SchedWakeUpAndPokeCpus that pokes
|
---|
| 2748 | * the Virtual CPU if it's still busy executing guest code.
|
---|
| 2749 | *
|
---|
| 2750 | * @returns VBox status code.
|
---|
| 2751 | * @retval VINF_SUCCESS if poked successfully.
|
---|
| 2752 | * @retval VINF_GVM_NOT_BUSY_IN_GC if the EMT wasn't busy in GC.
|
---|
| 2753 | *
|
---|
| 2754 | * @param pGVM The global (ring-0) VM structure.
|
---|
[58123] | 2755 | * @param pVCpu The cross context virtual CPU structure.
|
---|
[19406] | 2756 | */
|
---|
[80274] | 2757 | DECLINLINE(int) gvmmR0SchedPokeOne(PGVM pGVM, PVMCPUCC pVCpu)
|
---|
[19406] | 2758 | {
|
---|
[19454] | 2759 | pGVM->gvmm.s.StatsSched.cPokeCalls++;
|
---|
| 2760 |
|
---|
| 2761 | RTCPUID idHostCpu = pVCpu->idHostCpu;
|
---|
| 2762 | if ( idHostCpu == NIL_RTCPUID
|
---|
| 2763 | || VMCPU_GET_STATE(pVCpu) != VMCPUSTATE_STARTED_EXEC)
|
---|
| 2764 | {
|
---|
| 2765 | pGVM->gvmm.s.StatsSched.cPokeNotBusy++;
|
---|
[19406] | 2766 | return VINF_GVM_NOT_BUSY_IN_GC;
|
---|
[19454] | 2767 | }
|
---|
[19406] | 2768 |
|
---|
[24985] | 2769 | /* Note: this function is not implemented on Darwin and Linux (kernel < 2.6.19) */
|
---|
[19454] | 2770 | RTMpPokeCpu(idHostCpu);
|
---|
[19406] | 2771 | return VINF_SUCCESS;
|
---|
| 2772 | }
|
---|
| 2773 |
|
---|
[67989] | 2774 |
|
---|
[19406] | 2775 | /**
|
---|
| 2776 | * Pokes an EMT if it's still busy running guest code.
|
---|
| 2777 | *
|
---|
| 2778 | * @returns VBox status code.
|
---|
| 2779 | * @retval VINF_SUCCESS if poked successfully.
|
---|
| 2780 | * @retval VINF_GVM_NOT_BUSY_IN_GC if the EMT wasn't busy in GC.
|
---|
| 2781 | *
|
---|
[67989] | 2782 | * @param pGVM The global (ring-0) VM structure.
|
---|
[19406] | 2783 | * @param idCpu The ID of the virtual CPU to poke.
|
---|
[19652] | 2784 | * @param fTakeUsedLock Take the used lock or not
|
---|
[19406] | 2785 | */
|
---|
[80346] | 2786 | GVMMR0DECL(int) GVMMR0SchedPokeEx(PGVM pGVM, VMCPUID idCpu, bool fTakeUsedLock)
|
---|
[19406] | 2787 | {
|
---|
| 2788 | /*
|
---|
| 2789 | * Validate input and take the UsedLock.
|
---|
| 2790 | */
|
---|
| 2791 | PGVMM pGVMM;
|
---|
[80346] | 2792 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, fTakeUsedLock);
|
---|
[19406] | 2793 | if (RT_SUCCESS(rc))
|
---|
| 2794 | {
|
---|
| 2795 | if (idCpu < pGVM->cCpus)
|
---|
[78431] | 2796 | rc = gvmmR0SchedPokeOne(pGVM, &pGVM->aCpus[idCpu]);
|
---|
[19406] | 2797 | else
|
---|
| 2798 | rc = VERR_INVALID_CPU_ID;
|
---|
| 2799 |
|
---|
[19657] | 2800 | if (fTakeUsedLock)
|
---|
| 2801 | {
|
---|
[58830] | 2802 | int rc2 = GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
[19657] | 2803 | AssertRC(rc2);
|
---|
| 2804 | }
|
---|
[19406] | 2805 | }
|
---|
| 2806 |
|
---|
| 2807 | LogFlow(("GVMMR0SchedWakeUpAndPokeCpus: returns %Rrc\n", rc));
|
---|
| 2808 | return rc;
|
---|
[19652] | 2809 | }
|
---|
[19406] | 2810 |
|
---|
[19652] | 2811 |
|
---|
| 2812 | /**
|
---|
| 2813 | * Pokes an EMT if it's still busy running guest code.
|
---|
| 2814 | *
|
---|
| 2815 | * @returns VBox status code.
|
---|
| 2816 | * @retval VINF_SUCCESS if poked successfully.
|
---|
| 2817 | * @retval VINF_GVM_NOT_BUSY_IN_GC if the EMT wasn't busy in GC.
|
---|
| 2818 | *
|
---|
[67989] | 2819 | * @param pGVM The global (ring-0) VM structure.
|
---|
[19652] | 2820 | * @param idCpu The ID of the virtual CPU to poke.
|
---|
| 2821 | */
|
---|
[80346] | 2822 | GVMMR0DECL(int) GVMMR0SchedPoke(PGVM pGVM, VMCPUID idCpu)
|
---|
[19652] | 2823 | {
|
---|
[80346] | 2824 | return GVMMR0SchedPokeEx(pGVM, idCpu, true /* fTakeUsedLock */);
|
---|
[19406] | 2825 | }
|
---|
| 2826 |
|
---|
| 2827 |
|
---|
| 2828 | /**
|
---|
[67989] | 2829 | * Pokes an EMT if it's still busy running guest code, no GVM parameter and no
|
---|
| 2830 | * used locking.
|
---|
| 2831 | *
|
---|
| 2832 | * @returns VBox status code.
|
---|
| 2833 | * @retval VINF_SUCCESS if poked successfully.
|
---|
| 2834 | * @retval VINF_GVM_NOT_BUSY_IN_GC if the EMT wasn't busy in GC.
|
---|
| 2835 | *
|
---|
[80346] | 2836 | * @param pGVM The global (ring-0) VM structure.
|
---|
[67989] | 2837 | * @param idCpu The ID of the virtual CPU to poke.
|
---|
| 2838 | *
|
---|
| 2839 | * @deprecated Don't use in new code if possible! Use the GVM variant.
|
---|
| 2840 | */
|
---|
[80346] | 2841 | GVMMR0DECL(int) GVMMR0SchedPokeNoGVMNoLock(PGVM pGVM, VMCPUID idCpu)
|
---|
[67989] | 2842 | {
|
---|
| 2843 | PGVMM pGVMM;
|
---|
[80346] | 2844 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, false /*fTakeUsedLock*/);
|
---|
[67989] | 2845 | if (RT_SUCCESS(rc))
|
---|
| 2846 | {
|
---|
| 2847 | if (idCpu < pGVM->cCpus)
|
---|
[78431] | 2848 | rc = gvmmR0SchedPokeOne(pGVM, &pGVM->aCpus[idCpu]);
|
---|
[67989] | 2849 | else
|
---|
| 2850 | rc = VERR_INVALID_CPU_ID;
|
---|
| 2851 | }
|
---|
| 2852 | return rc;
|
---|
| 2853 | }
|
---|
| 2854 |
|
---|
| 2855 |
|
---|
| 2856 | /**
|
---|
[19406] | 2857 | * Wakes up a set of halted EMT threads so they can service pending request.
|
---|
| 2858 | *
|
---|
| 2859 | * @returns VBox status code, no informational stuff.
|
---|
| 2860 | *
|
---|
[67990] | 2861 | * @param pGVM The global (ring-0) VM structure.
|
---|
[19406] | 2862 | * @param pSleepSet The set of sleepers to wake up.
|
---|
| 2863 | * @param pPokeSet The set of CPUs to poke.
|
---|
| 2864 | */
|
---|
[80346] | 2865 | GVMMR0DECL(int) GVMMR0SchedWakeUpAndPokeCpus(PGVM pGVM, PCVMCPUSET pSleepSet, PCVMCPUSET pPokeSet)
|
---|
[19406] | 2866 | {
|
---|
| 2867 | AssertPtrReturn(pSleepSet, VERR_INVALID_POINTER);
|
---|
| 2868 | AssertPtrReturn(pPokeSet, VERR_INVALID_POINTER);
|
---|
| 2869 | RTNATIVETHREAD hSelf = RTThreadNativeSelf();
|
---|
| 2870 |
|
---|
| 2871 | /*
|
---|
| 2872 | * Validate input and take the UsedLock.
|
---|
| 2873 | */
|
---|
| 2874 | PGVMM pGVMM;
|
---|
[80346] | 2875 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, true /* fTakeUsedLock */);
|
---|
[19406] | 2876 | if (RT_SUCCESS(rc))
|
---|
| 2877 | {
|
---|
| 2878 | rc = VINF_SUCCESS;
|
---|
| 2879 | VMCPUID idCpu = pGVM->cCpus;
|
---|
| 2880 | while (idCpu-- > 0)
|
---|
| 2881 | {
|
---|
| 2882 | /* Don't try poke or wake up ourselves. */
|
---|
| 2883 | if (pGVM->aCpus[idCpu].hEMT == hSelf)
|
---|
| 2884 | continue;
|
---|
| 2885 |
|
---|
| 2886 | /* just ignore errors for now. */
|
---|
| 2887 | if (VMCPUSET_IS_PRESENT(pSleepSet, idCpu))
|
---|
| 2888 | gvmmR0SchedWakeUpOne(pGVM, &pGVM->aCpus[idCpu]);
|
---|
| 2889 | else if (VMCPUSET_IS_PRESENT(pPokeSet, idCpu))
|
---|
[78431] | 2890 | gvmmR0SchedPokeOne(pGVM, &pGVM->aCpus[idCpu]);
|
---|
[19406] | 2891 | }
|
---|
| 2892 |
|
---|
[58830] | 2893 | int rc2 = GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
[19406] | 2894 | AssertRC(rc2);
|
---|
| 2895 | }
|
---|
| 2896 |
|
---|
| 2897 | LogFlow(("GVMMR0SchedWakeUpAndPokeCpus: returns %Rrc\n", rc));
|
---|
| 2898 | return rc;
|
---|
| 2899 | }
|
---|
| 2900 |
|
---|
| 2901 |
|
---|
| 2902 | /**
|
---|
| 2903 | * VMMR0 request wrapper for GVMMR0SchedWakeUpAndPokeCpus.
|
---|
| 2904 | *
|
---|
| 2905 | * @returns see GVMMR0SchedWakeUpAndPokeCpus.
|
---|
[67990] | 2906 | * @param pGVM The global (ring-0) VM structure.
|
---|
[41777] | 2907 | * @param pReq Pointer to the request packet.
|
---|
[19406] | 2908 | */
|
---|
[80346] | 2909 | GVMMR0DECL(int) GVMMR0SchedWakeUpAndPokeCpusReq(PGVM pGVM, PGVMMSCHEDWAKEUPANDPOKECPUSREQ pReq)
|
---|
[19406] | 2910 | {
|
---|
| 2911 | /*
|
---|
| 2912 | * Validate input and pass it on.
|
---|
| 2913 | */
|
---|
| 2914 | AssertPtrReturn(pReq, VERR_INVALID_POINTER);
|
---|
| 2915 | AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
|
---|
| 2916 |
|
---|
[80346] | 2917 | return GVMMR0SchedWakeUpAndPokeCpus(pGVM, &pReq->SleepSet, &pReq->PokeSet);
|
---|
[19406] | 2918 | }
|
---|
| 2919 |
|
---|
| 2920 |
|
---|
| 2921 |
|
---|
| 2922 | /**
|
---|
[5167] | 2923 | * Poll the schedule to see if someone else should get a chance to run.
|
---|
| 2924 | *
|
---|
| 2925 | * This is a bit hackish and will not work too well if the machine is
|
---|
| 2926 | * under heavy load from non-VM processes.
|
---|
| 2927 | *
|
---|
| 2928 | * @returns VINF_SUCCESS if not yielded.
|
---|
| 2929 | * VINF_GVM_YIELDED if an attempt to switch to a different VM task was made.
|
---|
[67990] | 2930 | * @param pGVM The global (ring-0) VM structure.
|
---|
[58126] | 2931 | * @param idCpu The Virtual CPU ID of the calling EMT.
|
---|
| 2932 | * @param fYield Whether to yield or not.
|
---|
| 2933 | * This is for when we're spinning in the halt loop.
|
---|
[19395] | 2934 | * @thread EMT(idCpu).
|
---|
[5167] | 2935 | */
|
---|
[80346] | 2936 | GVMMR0DECL(int) GVMMR0SchedPoll(PGVM pGVM, VMCPUID idCpu, bool fYield)
|
---|
[5167] | 2937 | {
|
---|
| 2938 | /*
|
---|
| 2939 | * Validate input.
|
---|
| 2940 | */
|
---|
| 2941 | PGVMM pGVMM;
|
---|
[80346] | 2942 | int rc = gvmmR0ByGVMandEMT(pGVM, idCpu, &pGVMM);
|
---|
[5167] | 2943 | if (RT_SUCCESS(rc))
|
---|
| 2944 | {
|
---|
[58830] | 2945 | /*
|
---|
| 2946 | * We currently only implement helping doing wakeups (fYield = false), so don't
|
---|
| 2947 | * bother taking the lock if gvmmR0SchedDoWakeUps is not going to do anything.
|
---|
| 2948 | */
|
---|
| 2949 | if (!fYield && pGVMM->fDoEarlyWakeUps)
|
---|
| 2950 | {
|
---|
| 2951 | rc = GVMMR0_USED_SHARED_LOCK(pGVMM); AssertRC(rc);
|
---|
| 2952 | pGVM->gvmm.s.StatsSched.cPollCalls++;
|
---|
[5167] | 2953 |
|
---|
[58830] | 2954 | Assert(ASMGetFlags() & X86_EFL_IF);
|
---|
| 2955 | const uint64_t u64Now = RTTimeNanoTS(); /* (GIP time) */
|
---|
[5167] | 2956 |
|
---|
[5211] | 2957 | pGVM->gvmm.s.StatsSched.cPollWakeUps += gvmmR0SchedDoWakeUps(pGVMM, u64Now);
|
---|
[58830] | 2958 |
|
---|
| 2959 | GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
| 2960 | }
|
---|
| 2961 | /*
|
---|
| 2962 | * Not quite sure what we could do here...
|
---|
| 2963 | */
|
---|
| 2964 | else if (fYield)
|
---|
| 2965 | rc = VERR_NOT_IMPLEMENTED; /** @todo implement this... */
|
---|
[5167] | 2966 | else
|
---|
[58830] | 2967 | rc = VINF_SUCCESS;
|
---|
[5167] | 2968 | }
|
---|
| 2969 |
|
---|
| 2970 | LogFlow(("GVMMR0SchedWakeUp: returns %Rrc\n", rc));
|
---|
| 2971 | return rc;
|
---|
| 2972 | }
|
---|
| 2973 |
|
---|
[5211] | 2974 |
|
---|
[32572] | 2975 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 2976 | /**
|
---|
| 2977 | * Timer callback for the periodic preemption timer.
|
---|
| 2978 | *
|
---|
| 2979 | * @param pTimer The timer handle.
|
---|
| 2980 | * @param pvUser Pointer to the per cpu structure.
|
---|
| 2981 | * @param iTick The current tick.
|
---|
| 2982 | */
|
---|
| 2983 | static DECLCALLBACK(void) gvmmR0SchedPeriodicPreemptionTimerCallback(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
|
---|
| 2984 | {
|
---|
| 2985 | PGVMMHOSTCPU pCpu = (PGVMMHOSTCPU)pvUser;
|
---|
[39078] | 2986 | NOREF(pTimer); NOREF(iTick);
|
---|
[5211] | 2987 |
|
---|
[32572] | 2988 | /*
|
---|
| 2989 | * Termination check
|
---|
| 2990 | */
|
---|
| 2991 | if (pCpu->u32Magic != GVMMHOSTCPU_MAGIC)
|
---|
| 2992 | return;
|
---|
| 2993 |
|
---|
| 2994 | /*
|
---|
| 2995 | * Do the house keeping.
|
---|
| 2996 | */
|
---|
[40806] | 2997 | RTSpinlockAcquire(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 2998 |
|
---|
| 2999 | if (++pCpu->Ppt.iTickHistorization >= pCpu->Ppt.cTicksHistoriziationInterval)
|
---|
| 3000 | {
|
---|
| 3001 | /*
|
---|
| 3002 | * Historicize the max frequency.
|
---|
| 3003 | */
|
---|
| 3004 | uint32_t iHzHistory = ++pCpu->Ppt.iHzHistory % RT_ELEMENTS(pCpu->Ppt.aHzHistory);
|
---|
| 3005 | pCpu->Ppt.aHzHistory[iHzHistory] = pCpu->Ppt.uDesiredHz;
|
---|
| 3006 | pCpu->Ppt.iTickHistorization = 0;
|
---|
| 3007 | pCpu->Ppt.uDesiredHz = 0;
|
---|
| 3008 |
|
---|
| 3009 | /*
|
---|
| 3010 | * Check if the current timer frequency.
|
---|
| 3011 | */
|
---|
| 3012 | uint32_t uHistMaxHz = 0;
|
---|
| 3013 | for (uint32_t i = 0; i < RT_ELEMENTS(pCpu->Ppt.aHzHistory); i++)
|
---|
| 3014 | if (pCpu->Ppt.aHzHistory[i] > uHistMaxHz)
|
---|
| 3015 | uHistMaxHz = pCpu->Ppt.aHzHistory[i];
|
---|
| 3016 | if (uHistMaxHz == pCpu->Ppt.uTimerHz)
|
---|
[52618] | 3017 | RTSpinlockRelease(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3018 | else if (uHistMaxHz)
|
---|
| 3019 | {
|
---|
| 3020 | /*
|
---|
| 3021 | * Reprogram it.
|
---|
| 3022 | */
|
---|
| 3023 | pCpu->Ppt.cChanges++;
|
---|
| 3024 | pCpu->Ppt.iTickHistorization = 0;
|
---|
| 3025 | pCpu->Ppt.uTimerHz = uHistMaxHz;
|
---|
[33170] | 3026 | uint32_t const cNsInterval = RT_NS_1SEC / uHistMaxHz;
|
---|
[32572] | 3027 | pCpu->Ppt.cNsInterval = cNsInterval;
|
---|
| 3028 | if (cNsInterval < GVMMHOSTCPU_PPT_HIST_INTERVAL_NS)
|
---|
| 3029 | pCpu->Ppt.cTicksHistoriziationInterval = ( GVMMHOSTCPU_PPT_HIST_INTERVAL_NS
|
---|
| 3030 | + GVMMHOSTCPU_PPT_HIST_INTERVAL_NS / 2 - 1)
|
---|
| 3031 | / cNsInterval;
|
---|
| 3032 | else
|
---|
| 3033 | pCpu->Ppt.cTicksHistoriziationInterval = 1;
|
---|
[52618] | 3034 | RTSpinlockRelease(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3035 |
|
---|
| 3036 | /*SUPR0Printf("Cpu%u: change to %u Hz / %u ns\n", pCpu->idxCpuSet, uHistMaxHz, cNsInterval);*/
|
---|
| 3037 | RTTimerChangeInterval(pTimer, cNsInterval);
|
---|
| 3038 | }
|
---|
| 3039 | else
|
---|
| 3040 | {
|
---|
| 3041 | /*
|
---|
| 3042 | * Stop it.
|
---|
| 3043 | */
|
---|
| 3044 | pCpu->Ppt.fStarted = false;
|
---|
| 3045 | pCpu->Ppt.uTimerHz = 0;
|
---|
| 3046 | pCpu->Ppt.cNsInterval = 0;
|
---|
[52618] | 3047 | RTSpinlockRelease(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3048 |
|
---|
| 3049 | /*SUPR0Printf("Cpu%u: stopping (%u Hz)\n", pCpu->idxCpuSet, uHistMaxHz);*/
|
---|
| 3050 | RTTimerStop(pTimer);
|
---|
| 3051 | }
|
---|
| 3052 | }
|
---|
| 3053 | else
|
---|
[52618] | 3054 | RTSpinlockRelease(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3055 | }
|
---|
| 3056 | #endif /* GVMM_SCHED_WITH_PPT */
|
---|
| 3057 |
|
---|
| 3058 |
|
---|
[5211] | 3059 | /**
|
---|
[32489] | 3060 | * Updates the periodic preemption timer for the calling CPU.
|
---|
| 3061 | *
|
---|
| 3062 | * The caller must have disabled preemption!
|
---|
[32572] | 3063 | * The caller must check that the host can do high resolution timers.
|
---|
[32489] | 3064 | *
|
---|
[80346] | 3065 | * @param pGVM The global (ring-0) VM structure.
|
---|
[32489] | 3066 | * @param idHostCpu The current host CPU id.
|
---|
| 3067 | * @param uHz The desired frequency.
|
---|
| 3068 | */
|
---|
[80346] | 3069 | GVMMR0DECL(void) GVMMR0SchedUpdatePeriodicPreemptionTimer(PGVM pGVM, RTCPUID idHostCpu, uint32_t uHz)
|
---|
[32489] | 3070 | {
|
---|
[80346] | 3071 | NOREF(pGVM);
|
---|
[32572] | 3072 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 3073 | Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
|
---|
| 3074 | Assert(RTTimerCanDoHighResolution());
|
---|
[32489] | 3075 |
|
---|
[32572] | 3076 | /*
|
---|
| 3077 | * Resolve the per CPU data.
|
---|
| 3078 | */
|
---|
| 3079 | uint32_t iCpu = RTMpCpuIdToSetIndex(idHostCpu);
|
---|
| 3080 | PGVMM pGVMM = g_pGVMM;
|
---|
[80346] | 3081 | if ( !RT_VALID_PTR(pGVMM)
|
---|
[32572] | 3082 | || pGVMM->u32Magic != GVMM_MAGIC)
|
---|
| 3083 | return;
|
---|
| 3084 | AssertMsgReturnVoid(iCpu < pGVMM->cHostCpus, ("iCpu=%d cHostCpus=%d\n", iCpu, pGVMM->cHostCpus));
|
---|
| 3085 | PGVMMHOSTCPU pCpu = &pGVMM->aHostCpus[iCpu];
|
---|
| 3086 | AssertMsgReturnVoid( pCpu->u32Magic == GVMMHOSTCPU_MAGIC
|
---|
| 3087 | && pCpu->idCpu == idHostCpu,
|
---|
| 3088 | ("u32Magic=%#x idCpu=% idHostCpu=%d\n", pCpu->u32Magic, pCpu->idCpu, idHostCpu));
|
---|
| 3089 |
|
---|
| 3090 | /*
|
---|
| 3091 | * Check whether we need to do anything about the timer.
|
---|
| 3092 | * We have to be a little bit careful since we might be race the timer
|
---|
| 3093 | * callback here.
|
---|
| 3094 | */
|
---|
[32796] | 3095 | if (uHz > 16384)
|
---|
| 3096 | uHz = 16384; /** @todo add a query method for this! */
|
---|
[32572] | 3097 | if (RT_UNLIKELY( uHz > ASMAtomicReadU32(&pCpu->Ppt.uDesiredHz)
|
---|
| 3098 | && uHz >= pCpu->Ppt.uMinHz
|
---|
| 3099 | && !pCpu->Ppt.fStarting /* solaris paranoia */))
|
---|
| 3100 | {
|
---|
[40806] | 3101 | RTSpinlockAcquire(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3102 |
|
---|
| 3103 | pCpu->Ppt.uDesiredHz = uHz;
|
---|
| 3104 | uint32_t cNsInterval = 0;
|
---|
| 3105 | if (!pCpu->Ppt.fStarted)
|
---|
| 3106 | {
|
---|
| 3107 | pCpu->Ppt.cStarts++;
|
---|
| 3108 | pCpu->Ppt.fStarted = true;
|
---|
| 3109 | pCpu->Ppt.fStarting = true;
|
---|
| 3110 | pCpu->Ppt.iTickHistorization = 0;
|
---|
| 3111 | pCpu->Ppt.uTimerHz = uHz;
|
---|
[33170] | 3112 | pCpu->Ppt.cNsInterval = cNsInterval = RT_NS_1SEC / uHz;
|
---|
[32572] | 3113 | if (cNsInterval < GVMMHOSTCPU_PPT_HIST_INTERVAL_NS)
|
---|
| 3114 | pCpu->Ppt.cTicksHistoriziationInterval = ( GVMMHOSTCPU_PPT_HIST_INTERVAL_NS
|
---|
| 3115 | + GVMMHOSTCPU_PPT_HIST_INTERVAL_NS / 2 - 1)
|
---|
| 3116 | / cNsInterval;
|
---|
| 3117 | else
|
---|
| 3118 | pCpu->Ppt.cTicksHistoriziationInterval = 1;
|
---|
| 3119 | }
|
---|
| 3120 |
|
---|
[52618] | 3121 | RTSpinlockRelease(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3122 |
|
---|
| 3123 | if (cNsInterval)
|
---|
| 3124 | {
|
---|
| 3125 | RTTimerChangeInterval(pCpu->Ppt.pTimer, cNsInterval);
|
---|
| 3126 | int rc = RTTimerStart(pCpu->Ppt.pTimer, cNsInterval);
|
---|
| 3127 | AssertRC(rc);
|
---|
| 3128 |
|
---|
[40806] | 3129 | RTSpinlockAcquire(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3130 | if (RT_FAILURE(rc))
|
---|
| 3131 | pCpu->Ppt.fStarted = false;
|
---|
| 3132 | pCpu->Ppt.fStarting = false;
|
---|
[52618] | 3133 | RTSpinlockRelease(pCpu->Ppt.hSpinlock);
|
---|
[32572] | 3134 | }
|
---|
| 3135 | }
|
---|
[39078] | 3136 | #else /* !GVMM_SCHED_WITH_PPT */
|
---|
| 3137 | NOREF(idHostCpu); NOREF(uHz);
|
---|
| 3138 | #endif /* !GVMM_SCHED_WITH_PPT */
|
---|
[32489] | 3139 | }
|
---|
| 3140 |
|
---|
| 3141 |
|
---|
| 3142 | /**
|
---|
[82989] | 3143 | * Calls @a pfnCallback for each VM in the system.
|
---|
| 3144 | *
|
---|
| 3145 | * This will enumerate the VMs while holding the global VM used list lock in
|
---|
| 3146 | * shared mode. So, only suitable for simple work. If more expensive work
|
---|
| 3147 | * needs doing, a different approach must be taken as using this API would
|
---|
| 3148 | * otherwise block VM creation and destruction.
|
---|
| 3149 | *
|
---|
| 3150 | * @returns VBox status code.
|
---|
| 3151 | * @param pfnCallback The callback function.
|
---|
| 3152 | * @param pvUser User argument to the callback.
|
---|
| 3153 | */
|
---|
| 3154 | GVMMR0DECL(int) GVMMR0EnumVMs(PFNGVMMR0ENUMCALLBACK pfnCallback, void *pvUser)
|
---|
| 3155 | {
|
---|
| 3156 | PGVMM pGVMM;
|
---|
| 3157 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
| 3158 |
|
---|
| 3159 | int rc = VINF_SUCCESS;
|
---|
| 3160 | GVMMR0_USED_SHARED_LOCK(pGVMM);
|
---|
| 3161 | for (unsigned i = pGVMM->iUsedHead, cLoops = 0;
|
---|
| 3162 | i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
|
---|
| 3163 | i = pGVMM->aHandles[i].iNext, cLoops++)
|
---|
| 3164 | {
|
---|
| 3165 | PGVM pGVM = pGVMM->aHandles[i].pGVM;
|
---|
| 3166 | if ( RT_VALID_PTR(pGVM)
|
---|
| 3167 | && RT_VALID_PTR(pGVMM->aHandles[i].pvObj)
|
---|
| 3168 | && pGVM->u32Magic == GVM_MAGIC)
|
---|
| 3169 | {
|
---|
| 3170 | rc = pfnCallback(pGVM, pvUser);
|
---|
| 3171 | if (rc != VINF_SUCCESS)
|
---|
| 3172 | break;
|
---|
| 3173 | }
|
---|
| 3174 |
|
---|
| 3175 | AssertBreak(cLoops < RT_ELEMENTS(pGVMM->aHandles) * 4); /* paranoia */
|
---|
| 3176 | }
|
---|
| 3177 | GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
| 3178 | return rc;
|
---|
| 3179 | }
|
---|
| 3180 |
|
---|
| 3181 |
|
---|
| 3182 | /**
|
---|
[5211] | 3183 | * Retrieves the GVMM statistics visible to the caller.
|
---|
| 3184 | *
|
---|
| 3185 | * @returns VBox status code.
|
---|
| 3186 | *
|
---|
| 3187 | * @param pStats Where to put the statistics.
|
---|
| 3188 | * @param pSession The current session.
|
---|
[67991] | 3189 | * @param pGVM The GVM to obtain statistics for. Optional.
|
---|
[5211] | 3190 | */
|
---|
[80346] | 3191 | GVMMR0DECL(int) GVMMR0QueryStatistics(PGVMMSTATS pStats, PSUPDRVSESSION pSession, PGVM pGVM)
|
---|
[5211] | 3192 | {
|
---|
[80346] | 3193 | LogFlow(("GVMMR0QueryStatistics: pStats=%p pSession=%p pGVM=%p\n", pStats, pSession, pGVM));
|
---|
[5211] | 3194 |
|
---|
| 3195 | /*
|
---|
| 3196 | * Validate input.
|
---|
| 3197 | */
|
---|
| 3198 | AssertPtrReturn(pSession, VERR_INVALID_POINTER);
|
---|
| 3199 | AssertPtrReturn(pStats, VERR_INVALID_POINTER);
|
---|
| 3200 | pStats->cVMs = 0; /* (crash before taking the sem...) */
|
---|
| 3201 |
|
---|
| 3202 | /*
|
---|
| 3203 | * Take the lock and get the VM statistics.
|
---|
| 3204 | */
|
---|
| 3205 | PGVMM pGVMM;
|
---|
[67991] | 3206 | if (pGVM)
|
---|
[5211] | 3207 | {
|
---|
[80346] | 3208 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, true /*fTakeUsedLock*/);
|
---|
[5211] | 3209 | if (RT_FAILURE(rc))
|
---|
| 3210 | return rc;
|
---|
| 3211 | pStats->SchedVM = pGVM->gvmm.s.StatsSched;
|
---|
[92892] | 3212 |
|
---|
| 3213 | uint32_t iCpu = RT_MIN(pGVM->cCpus, RT_ELEMENTS(pStats->aVCpus));
|
---|
| 3214 | if (iCpu < RT_ELEMENTS(pStats->aVCpus))
|
---|
| 3215 | RT_BZERO(&pStats->aVCpus[iCpu], (RT_ELEMENTS(pStats->aVCpus) - iCpu) * sizeof(pStats->aVCpus[0]));
|
---|
| 3216 | while (iCpu-- > 0)
|
---|
| 3217 | pStats->aVCpus[iCpu] = pGVM->aCpus[iCpu].gvmm.s.Stats;
|
---|
[5211] | 3218 | }
|
---|
| 3219 | else
|
---|
| 3220 | {
|
---|
[39402] | 3221 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
[92892] | 3222 | RT_ZERO(pStats->SchedVM);
|
---|
| 3223 | RT_ZERO(pStats->aVCpus);
|
---|
[5211] | 3224 |
|
---|
[58830] | 3225 | int rc = GVMMR0_USED_SHARED_LOCK(pGVMM);
|
---|
[5211] | 3226 | AssertRCReturn(rc, rc);
|
---|
| 3227 | }
|
---|
| 3228 |
|
---|
| 3229 | /*
|
---|
[33540] | 3230 | * Enumerate the VMs and add the ones visible to the statistics.
|
---|
[5211] | 3231 | */
|
---|
| 3232 | pStats->cVMs = 0;
|
---|
[19707] | 3233 | pStats->cEMTs = 0;
|
---|
[5211] | 3234 | memset(&pStats->SchedSum, 0, sizeof(pStats->SchedSum));
|
---|
| 3235 |
|
---|
| 3236 | for (unsigned i = pGVMM->iUsedHead;
|
---|
| 3237 | i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
|
---|
| 3238 | i = pGVMM->aHandles[i].iNext)
|
---|
| 3239 | {
|
---|
[67992] | 3240 | PGVM pOtherGVM = pGVMM->aHandles[i].pGVM;
|
---|
[5211] | 3241 | void *pvObj = pGVMM->aHandles[i].pvObj;
|
---|
[80346] | 3242 | if ( RT_VALID_PTR(pvObj)
|
---|
| 3243 | && RT_VALID_PTR(pOtherGVM)
|
---|
[67992] | 3244 | && pOtherGVM->u32Magic == GVM_MAGIC
|
---|
[5211] | 3245 | && RT_SUCCESS(SUPR0ObjVerifyAccess(pvObj, pSession, NULL)))
|
---|
| 3246 | {
|
---|
| 3247 | pStats->cVMs++;
|
---|
[67992] | 3248 | pStats->cEMTs += pOtherGVM->cCpus;
|
---|
[5211] | 3249 |
|
---|
[67992] | 3250 | pStats->SchedSum.cHaltCalls += pOtherGVM->gvmm.s.StatsSched.cHaltCalls;
|
---|
| 3251 | pStats->SchedSum.cHaltBlocking += pOtherGVM->gvmm.s.StatsSched.cHaltBlocking;
|
---|
| 3252 | pStats->SchedSum.cHaltTimeouts += pOtherGVM->gvmm.s.StatsSched.cHaltTimeouts;
|
---|
| 3253 | pStats->SchedSum.cHaltNotBlocking += pOtherGVM->gvmm.s.StatsSched.cHaltNotBlocking;
|
---|
| 3254 | pStats->SchedSum.cHaltWakeUps += pOtherGVM->gvmm.s.StatsSched.cHaltWakeUps;
|
---|
[5211] | 3255 |
|
---|
[67992] | 3256 | pStats->SchedSum.cWakeUpCalls += pOtherGVM->gvmm.s.StatsSched.cWakeUpCalls;
|
---|
| 3257 | pStats->SchedSum.cWakeUpNotHalted += pOtherGVM->gvmm.s.StatsSched.cWakeUpNotHalted;
|
---|
| 3258 | pStats->SchedSum.cWakeUpWakeUps += pOtherGVM->gvmm.s.StatsSched.cWakeUpWakeUps;
|
---|
[5211] | 3259 |
|
---|
[67992] | 3260 | pStats->SchedSum.cPokeCalls += pOtherGVM->gvmm.s.StatsSched.cPokeCalls;
|
---|
| 3261 | pStats->SchedSum.cPokeNotBusy += pOtherGVM->gvmm.s.StatsSched.cPokeNotBusy;
|
---|
[19454] | 3262 |
|
---|
[67992] | 3263 | pStats->SchedSum.cPollCalls += pOtherGVM->gvmm.s.StatsSched.cPollCalls;
|
---|
| 3264 | pStats->SchedSum.cPollHalts += pOtherGVM->gvmm.s.StatsSched.cPollHalts;
|
---|
| 3265 | pStats->SchedSum.cPollWakeUps += pOtherGVM->gvmm.s.StatsSched.cPollWakeUps;
|
---|
[5211] | 3266 | }
|
---|
| 3267 | }
|
---|
| 3268 |
|
---|
[32796] | 3269 | /*
|
---|
| 3270 | * Copy out the per host CPU statistics.
|
---|
| 3271 | */
|
---|
| 3272 | uint32_t iDstCpu = 0;
|
---|
| 3273 | uint32_t cSrcCpus = pGVMM->cHostCpus;
|
---|
| 3274 | for (uint32_t iSrcCpu = 0; iSrcCpu < cSrcCpus; iSrcCpu++)
|
---|
| 3275 | {
|
---|
| 3276 | if (pGVMM->aHostCpus[iSrcCpu].idCpu != NIL_RTCPUID)
|
---|
| 3277 | {
|
---|
| 3278 | pStats->aHostCpus[iDstCpu].idCpu = pGVMM->aHostCpus[iSrcCpu].idCpu;
|
---|
| 3279 | pStats->aHostCpus[iDstCpu].idxCpuSet = pGVMM->aHostCpus[iSrcCpu].idxCpuSet;
|
---|
| 3280 | #ifdef GVMM_SCHED_WITH_PPT
|
---|
| 3281 | pStats->aHostCpus[iDstCpu].uDesiredHz = pGVMM->aHostCpus[iSrcCpu].Ppt.uDesiredHz;
|
---|
| 3282 | pStats->aHostCpus[iDstCpu].uTimerHz = pGVMM->aHostCpus[iSrcCpu].Ppt.uTimerHz;
|
---|
| 3283 | pStats->aHostCpus[iDstCpu].cChanges = pGVMM->aHostCpus[iSrcCpu].Ppt.cChanges;
|
---|
| 3284 | pStats->aHostCpus[iDstCpu].cStarts = pGVMM->aHostCpus[iSrcCpu].Ppt.cStarts;
|
---|
| 3285 | #else
|
---|
| 3286 | pStats->aHostCpus[iDstCpu].uDesiredHz = 0;
|
---|
| 3287 | pStats->aHostCpus[iDstCpu].uTimerHz = 0;
|
---|
| 3288 | pStats->aHostCpus[iDstCpu].cChanges = 0;
|
---|
| 3289 | pStats->aHostCpus[iDstCpu].cStarts = 0;
|
---|
| 3290 | #endif
|
---|
| 3291 | iDstCpu++;
|
---|
[36235] | 3292 | if (iDstCpu >= RT_ELEMENTS(pStats->aHostCpus))
|
---|
| 3293 | break;
|
---|
[32796] | 3294 | }
|
---|
| 3295 | }
|
---|
| 3296 | pStats->cHostCpus = iDstCpu;
|
---|
| 3297 |
|
---|
[58830] | 3298 | GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
[5211] | 3299 |
|
---|
| 3300 | return VINF_SUCCESS;
|
---|
| 3301 | }
|
---|
| 3302 |
|
---|
| 3303 |
|
---|
| 3304 | /**
|
---|
| 3305 | * VMMR0 request wrapper for GVMMR0QueryStatistics.
|
---|
| 3306 | *
|
---|
| 3307 | * @returns see GVMMR0QueryStatistics.
|
---|
[67991] | 3308 | * @param pGVM The global (ring-0) VM structure. Optional.
|
---|
[41777] | 3309 | * @param pReq Pointer to the request packet.
|
---|
[67955] | 3310 | * @param pSession The current session.
|
---|
[5211] | 3311 | */
|
---|
[80346] | 3312 | GVMMR0DECL(int) GVMMR0QueryStatisticsReq(PGVM pGVM, PGVMMQUERYSTATISTICSSREQ pReq, PSUPDRVSESSION pSession)
|
---|
[5211] | 3313 | {
|
---|
| 3314 | /*
|
---|
| 3315 | * Validate input and pass it on.
|
---|
| 3316 | */
|
---|
| 3317 | AssertPtrReturn(pReq, VERR_INVALID_POINTER);
|
---|
| 3318 | AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
|
---|
[67955] | 3319 | AssertReturn(pReq->pSession == pSession, VERR_INVALID_PARAMETER);
|
---|
[5211] | 3320 |
|
---|
[80346] | 3321 | return GVMMR0QueryStatistics(&pReq->Stats, pSession, pGVM);
|
---|
[5211] | 3322 | }
|
---|
| 3323 |
|
---|
[5233] | 3324 |
|
---|
| 3325 | /**
|
---|
| 3326 | * Resets the specified GVMM statistics.
|
---|
| 3327 | *
|
---|
| 3328 | * @returns VBox status code.
|
---|
| 3329 | *
|
---|
| 3330 | * @param pStats Which statistics to reset, that is, non-zero fields indicates which to reset.
|
---|
| 3331 | * @param pSession The current session.
|
---|
[67991] | 3332 | * @param pGVM The GVM to reset statistics for. Optional.
|
---|
[5233] | 3333 | */
|
---|
[80346] | 3334 | GVMMR0DECL(int) GVMMR0ResetStatistics(PCGVMMSTATS pStats, PSUPDRVSESSION pSession, PGVM pGVM)
|
---|
[5233] | 3335 | {
|
---|
[80346] | 3336 | LogFlow(("GVMMR0ResetStatistics: pStats=%p pSession=%p pGVM=%p\n", pStats, pSession, pGVM));
|
---|
[5233] | 3337 |
|
---|
| 3338 | /*
|
---|
| 3339 | * Validate input.
|
---|
| 3340 | */
|
---|
| 3341 | AssertPtrReturn(pSession, VERR_INVALID_POINTER);
|
---|
| 3342 | AssertPtrReturn(pStats, VERR_INVALID_POINTER);
|
---|
| 3343 |
|
---|
| 3344 | /*
|
---|
| 3345 | * Take the lock and get the VM statistics.
|
---|
| 3346 | */
|
---|
| 3347 | PGVMM pGVMM;
|
---|
[67991] | 3348 | if (pGVM)
|
---|
[5233] | 3349 | {
|
---|
[80346] | 3350 | int rc = gvmmR0ByGVM(pGVM, &pGVMM, true /*fTakeUsedLock*/);
|
---|
[5233] | 3351 | if (RT_FAILURE(rc))
|
---|
| 3352 | return rc;
|
---|
| 3353 | # define MAYBE_RESET_FIELD(field) \
|
---|
| 3354 | do { if (pStats->SchedVM. field ) { pGVM->gvmm.s.StatsSched. field = 0; } } while (0)
|
---|
| 3355 | MAYBE_RESET_FIELD(cHaltCalls);
|
---|
| 3356 | MAYBE_RESET_FIELD(cHaltBlocking);
|
---|
| 3357 | MAYBE_RESET_FIELD(cHaltTimeouts);
|
---|
| 3358 | MAYBE_RESET_FIELD(cHaltNotBlocking);
|
---|
| 3359 | MAYBE_RESET_FIELD(cHaltWakeUps);
|
---|
| 3360 | MAYBE_RESET_FIELD(cWakeUpCalls);
|
---|
| 3361 | MAYBE_RESET_FIELD(cWakeUpNotHalted);
|
---|
| 3362 | MAYBE_RESET_FIELD(cWakeUpWakeUps);
|
---|
[19454] | 3363 | MAYBE_RESET_FIELD(cPokeCalls);
|
---|
| 3364 | MAYBE_RESET_FIELD(cPokeNotBusy);
|
---|
[5233] | 3365 | MAYBE_RESET_FIELD(cPollCalls);
|
---|
| 3366 | MAYBE_RESET_FIELD(cPollHalts);
|
---|
| 3367 | MAYBE_RESET_FIELD(cPollWakeUps);
|
---|
| 3368 | # undef MAYBE_RESET_FIELD
|
---|
| 3369 | }
|
---|
| 3370 | else
|
---|
| 3371 | {
|
---|
[39402] | 3372 | GVMM_GET_VALID_INSTANCE(pGVMM, VERR_GVMM_INSTANCE);
|
---|
[5233] | 3373 |
|
---|
[58830] | 3374 | int rc = GVMMR0_USED_SHARED_LOCK(pGVMM);
|
---|
[5233] | 3375 | AssertRCReturn(rc, rc);
|
---|
| 3376 | }
|
---|
| 3377 |
|
---|
| 3378 | /*
|
---|
[33540] | 3379 | * Enumerate the VMs and add the ones visible to the statistics.
|
---|
[5233] | 3380 | */
|
---|
[59747] | 3381 | if (!ASMMemIsZero(&pStats->SchedSum, sizeof(pStats->SchedSum)))
|
---|
[5233] | 3382 | {
|
---|
| 3383 | for (unsigned i = pGVMM->iUsedHead;
|
---|
| 3384 | i != NIL_GVM_HANDLE && i < RT_ELEMENTS(pGVMM->aHandles);
|
---|
| 3385 | i = pGVMM->aHandles[i].iNext)
|
---|
| 3386 | {
|
---|
[67992] | 3387 | PGVM pOtherGVM = pGVMM->aHandles[i].pGVM;
|
---|
[5233] | 3388 | void *pvObj = pGVMM->aHandles[i].pvObj;
|
---|
[80346] | 3389 | if ( RT_VALID_PTR(pvObj)
|
---|
| 3390 | && RT_VALID_PTR(pOtherGVM)
|
---|
[67992] | 3391 | && pOtherGVM->u32Magic == GVM_MAGIC
|
---|
[5233] | 3392 | && RT_SUCCESS(SUPR0ObjVerifyAccess(pvObj, pSession, NULL)))
|
---|
| 3393 | {
|
---|
| 3394 | # define MAYBE_RESET_FIELD(field) \
|
---|
[67992] | 3395 | do { if (pStats->SchedSum. field ) { pOtherGVM->gvmm.s.StatsSched. field = 0; } } while (0)
|
---|
[5233] | 3396 | MAYBE_RESET_FIELD(cHaltCalls);
|
---|
| 3397 | MAYBE_RESET_FIELD(cHaltBlocking);
|
---|
| 3398 | MAYBE_RESET_FIELD(cHaltTimeouts);
|
---|
| 3399 | MAYBE_RESET_FIELD(cHaltNotBlocking);
|
---|
| 3400 | MAYBE_RESET_FIELD(cHaltWakeUps);
|
---|
| 3401 | MAYBE_RESET_FIELD(cWakeUpCalls);
|
---|
| 3402 | MAYBE_RESET_FIELD(cWakeUpNotHalted);
|
---|
| 3403 | MAYBE_RESET_FIELD(cWakeUpWakeUps);
|
---|
[19454] | 3404 | MAYBE_RESET_FIELD(cPokeCalls);
|
---|
| 3405 | MAYBE_RESET_FIELD(cPokeNotBusy);
|
---|
[5233] | 3406 | MAYBE_RESET_FIELD(cPollCalls);
|
---|
| 3407 | MAYBE_RESET_FIELD(cPollHalts);
|
---|
| 3408 | MAYBE_RESET_FIELD(cPollWakeUps);
|
---|
| 3409 | # undef MAYBE_RESET_FIELD
|
---|
| 3410 | }
|
---|
| 3411 | }
|
---|
| 3412 | }
|
---|
| 3413 |
|
---|
[58830] | 3414 | GVMMR0_USED_SHARED_UNLOCK(pGVMM);
|
---|
[5233] | 3415 |
|
---|
| 3416 | return VINF_SUCCESS;
|
---|
| 3417 | }
|
---|
| 3418 |
|
---|
| 3419 |
|
---|
| 3420 | /**
|
---|
| 3421 | * VMMR0 request wrapper for GVMMR0ResetStatistics.
|
---|
| 3422 | *
|
---|
| 3423 | * @returns see GVMMR0ResetStatistics.
|
---|
[67991] | 3424 | * @param pGVM The global (ring-0) VM structure. Optional.
|
---|
[41777] | 3425 | * @param pReq Pointer to the request packet.
|
---|
[67955] | 3426 | * @param pSession The current session.
|
---|
[5233] | 3427 | */
|
---|
[80346] | 3428 | GVMMR0DECL(int) GVMMR0ResetStatisticsReq(PGVM pGVM, PGVMMRESETSTATISTICSSREQ pReq, PSUPDRVSESSION pSession)
|
---|
[5233] | 3429 | {
|
---|
| 3430 | /*
|
---|
| 3431 | * Validate input and pass it on.
|
---|
| 3432 | */
|
---|
| 3433 | AssertPtrReturn(pReq, VERR_INVALID_POINTER);
|
---|
| 3434 | AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
|
---|
[67955] | 3435 | AssertReturn(pReq->pSession == pSession, VERR_INVALID_PARAMETER);
|
---|
[5233] | 3436 |
|
---|
[80346] | 3437 | return GVMMR0ResetStatistics(&pReq->Stats, pSession, pGVM);
|
---|
[5233] | 3438 | }
|
---|
| 3439 |
|
---|