VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp

Last change on this file was 99829, checked in by vboxsync, 12 months ago

*: A bunch of adjustments that allows using /permissive- with Visual C++ (qt 6.x necessity). [scm fixes]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.5 KB
RevLine 
[28432]1/* $Id: VBoxServicePageSharing.cpp 99829 2023-05-17 13:51:57Z vboxsync $ */
2/** @file
[28733]3 * VBoxService - Guest page sharing.
[28432]4 */
5
6/*
[98103]7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
[28432]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
[28432]26 */
27
[58052]28
[58033]29/** @page pg_vgsvc_pagesharing VBoxService - Page Sharing
30 *
[58052]31 * The Page Sharing subservice is responsible for finding memory mappings
32 * suitable page fusions.
[58033]33 *
[58052]34 * It is the driving force behind the Page Fusion feature in VirtualBox.
35 * Working with PGM and GMM (ring-0) thru the VMMDev interface. Every so often
36 * it reenumerates the memory mappings (executables and shared libraries) of the
37 * guest OS and reports additions and removals to GMM. For each mapping there
38 * is a filename and version as well as and address range and subsections. GMM
39 * will match the mapping with mapping with the same name and version from other
40 * VMs and see if there are any identical pages between the two.
41 *
42 * To increase the hit rate and reduce the volatility, the service launches a
43 * child process which loads all the Windows system DLLs it can. The child
44 * process is necessary as the DLLs are loaded without running the init code,
45 * and therefore not actually callable for other VBoxService code (may crash).
46 *
[58033]47 * This is currently only implemented on Windows. There is no technical reason
48 * for it not to be doable for all the other guests too, it's just a matter of
49 * customer demand and engineering time.
[58052]50 *
[58033]51 */
[28432]52
[58033]53
[57358]54/*********************************************************************************************************************************
55* Header Files *
56*********************************************************************************************************************************/
[28432]57#include <iprt/assert.h>
58#include <iprt/avl.h>
[29640]59#include <iprt/asm.h>
[28432]60#include <iprt/mem.h>
[46593]61#include <iprt/ldr.h>
[30560]62#include <iprt/process.h>
63#include <iprt/env.h>
[28432]64#include <iprt/stream.h>
[30036]65#include <iprt/file.h>
[28432]66#include <iprt/string.h>
67#include <iprt/semaphore.h>
[62851]68#include <iprt/string.h>
[28432]69#include <iprt/system.h>
70#include <iprt/thread.h>
71#include <iprt/time.h>
[76422]72#include <VBox/err.h>
[68660]73#include <VBox/VMMDev.h>
[28432]74#include <VBox/VBoxGuestLib.h>
75
[70171]76#ifdef RT_OS_WINDOWS
77#include <iprt/nt/nt-and-windows.h>
[58029]78# include <tlhelp32.h>
79# include <psapi.h>
80# include <winternl.h>
81#endif
[28432]82
[70171]83#include "VBoxServiceInternal.h"
84#include "VBoxServiceUtils.h"
[58029]85
[70171]86
[57358]87/*********************************************************************************************************************************
[58029]88* Header Files *
[57358]89*********************************************************************************************************************************/
[28432]90typedef struct
91{
92 AVLPVNODECORE Core;
[58029]93#ifdef RT_OS_WINDOWS
[28432]94 HMODULE hModule;
95 char szFileVersion[16];
96 MODULEENTRY32 Info;
[70171]97#endif
[58029]98} VGSVCPGSHKNOWNMOD, *PVGSVCPGSHKNOWNMOD;
[28432]99
[29952]100
[58029]101/*********************************************************************************************************************************
102* Global Variables *
103*********************************************************************************************************************************/
104/** The semaphore we're blocking on. */
105static RTSEMEVENTMULTI g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
[29952]106
[30668]107static PAVLPVNODECORE g_pKnownModuleTree = NULL;
[70171]108static uint64_t g_idSession = 0;
[28432]109
[58029]110
111/*********************************************************************************************************************************
112* Internal Functions *
113*********************************************************************************************************************************/
114static DECLCALLBACK(int) vgsvcPageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *pvUser);
115
116
[70171]117#ifdef RT_OS_WINDOWS
[58029]118
[28432]119/**
120 * Registers a new module with the VMM
[29952]121 * @param pModule Module ptr
[99829]122 * @param fValidateMemory Validate/touch memory pages or not
[99828]123 * @param pVersionInfo Version info bytes.
[28432]124 */
[99828]125static void vgsvcPageSharingRegisterModuleInner(PVGSVCPGSHKNOWNMOD pModule, bool fValidateMemory, BYTE *pVersionInfo)
[28432]126{
127 VMMDEVSHAREDREGIONDESC aRegions[VMMDEVSHAREDREGIONDESC_MAX];
128 DWORD dwModuleSize = pModule->Info.modBaseSize;
129 BYTE *pBaseAddress = pModule->Info.modBaseAddr;
130
131 /* Fetch default code page. */
[58029]132 struct LANGANDCODEPAGE
133 {
[28432]134 WORD wLanguage;
135 WORD wCodePage;
136 } *lpTranslate;
137
[70171]138 UINT cbTranslate;
[58029]139 BOOL fRet = VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"), (LPVOID *)&lpTranslate, &cbTranslate);
140 if ( !fRet
141 || cbTranslate < 4)
[28432]142 {
[58029]143 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VerQueryValue failed with %d (cb=%d)\n", GetLastError(), cbTranslate);
[99828]144 return;
[28432]145 }
146
147 unsigned i;
148 UINT cbFileVersion;
[62851]149 char *pszFileVersion = NULL; /* Shut up MSC */
[28432]150 unsigned cTranslationBlocks = cbTranslate/sizeof(struct LANGANDCODEPAGE);
151
[62851]152 pModule->szFileVersion[0] = '\0';
153 for (i = 0; i < cTranslationBlocks; i++)
[28432]154 {
155 /* Fetch file version string. */
[99828]156 char szFileVersionLocation[256];
[28432]157
[62851]158/** @todo r=bird: Mixing ANSI and TCHAR crap again. This code is a mess. We
159 * always use the wide version of the API and convert to UTF-8/whatever. */
160
[95851]161 RTStrPrintf(szFileVersionLocation, sizeof(szFileVersionLocation),
162 "\\StringFileInfo\\%04x%04x\\FileVersion", lpTranslate[i].wLanguage, lpTranslate[i].wCodePage);
[62851]163 fRet = VerQueryValue(pVersionInfo, szFileVersionLocation, (LPVOID *)&pszFileVersion, &cbFileVersion);
[58029]164 if (fRet)
[62851]165 {
166 RTStrCopy(pModule->szFileVersion, sizeof(pModule->szFileVersion), pszFileVersion);
[28432]167 break;
[62851]168 }
[28432]169 }
170 if (i == cTranslationBlocks)
171 {
[58029]172 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: no file version found!\n");
[99828]173 return;
[28432]174 }
175
176 unsigned idxRegion = 0;
[29952]177 if (fValidateMemory)
[28432]178 {
[29952]179 do
[28432]180 {
[29952]181 MEMORY_BASIC_INFORMATION MemInfo;
[58029]182 SIZE_T cbRet = VirtualQuery(pBaseAddress, &MemInfo, sizeof(MemInfo));
183 Assert(cbRet);
184 if (!cbRet)
[28432]185 {
[58029]186 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VirtualQueryEx failed with %d\n", GetLastError());
[29952]187 break;
188 }
189
[58029]190 if ( MemInfo.State == MEM_COMMIT
191 && MemInfo.Type == MEM_IMAGE)
[29473]192 {
[29952]193 switch (MemInfo.Protect)
194 {
[58029]195 case PAGE_EXECUTE:
196 case PAGE_EXECUTE_READ:
197 case PAGE_READONLY:
198 {
199 char *pRegion = (char *)MemInfo.BaseAddress;
[29473]200
[58029]201 /* Skip the first region as it only contains the image file header. */
202 if (pRegion != (char *)pModule->Info.modBaseAddr)
[29952]203 {
[58029]204 /* Touch all pages. */
205 while ((uintptr_t)pRegion < (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize)
206 {
207 /* Try to trick the optimizer to leave the page touching code in place. */
208 ASMProbeReadByte(pRegion);
209 pRegion += PAGE_SIZE;
210 }
[29952]211 }
[29993]212#ifdef RT_ARCH_X86
[58029]213 aRegions[idxRegion].GCRegionAddr = (RTGCPTR32)MemInfo.BaseAddress;
[29993]214#else
[58029]215 aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)MemInfo.BaseAddress;
[29991]216#endif
[58029]217 aRegions[idxRegion].cbRegion = MemInfo.RegionSize;
218 idxRegion++;
[29952]219
[58029]220 break;
221 }
[29473]222
[58029]223 default:
224 break; /* ignore */
[29952]225 }
[29473]226 }
[28432]227
[29952]228 pBaseAddress = (BYTE *)MemInfo.BaseAddress + MemInfo.RegionSize;
229 if (dwModuleSize > MemInfo.RegionSize)
230 dwModuleSize -= MemInfo.RegionSize;
231 else
232 {
233 dwModuleSize = 0;
234 break;
235 }
[28434]236
[29952]237 if (idxRegion >= RT_ELEMENTS(aRegions))
238 break; /* out of room */
[99828]239 } while (dwModuleSize);
[28432]240 }
[29952]241 else
242 {
243 /* We can't probe kernel memory ranges, so pretend it's one big region. */
[29993]244#ifdef RT_ARCH_X86
245 aRegions[idxRegion].GCRegionAddr = (RTGCPTR32)pBaseAddress;
246#else
[29990]247 aRegions[idxRegion].GCRegionAddr = (RTGCPTR64)pBaseAddress;
248#endif
[29952]249 aRegions[idxRegion].cbRegion = dwModuleSize;
250 idxRegion++;
251 }
[99828]252 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VbglR3RegisterSharedModule %s %s base=%p size=%x cregions=%d\n",
253 pModule->Info.szModule, pModule->szFileVersion, pModule->Info.modBaseAddr, pModule->Info.modBaseSize, idxRegion);
[40054]254 int rc = VbglR3RegisterSharedModule(pModule->Info.szModule, pModule->szFileVersion, (uintptr_t)pModule->Info.modBaseAddr,
[29996]255 pModule->Info.modBaseSize, idxRegion, aRegions);
[29365]256 if (RT_FAILURE(rc))
[58029]257 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: VbglR3RegisterSharedModule failed with %Rrc\n", rc);
[99828]258}
[30013]259
[99828]260
261/**
262 * Registers a new module with the VMM
263 * @param pModule Module ptr
264 * @param fValidateMemory Validate/touch memory pages or not
265 */
266static void vgsvcPageSharingRegisterModule(PVGSVCPGSHKNOWNMOD pModule, bool fValidateMemory)
267{
268 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule\n");
269
270 DWORD dwDummy;
271 DWORD cbVersion = GetFileVersionInfoSize(pModule->Info.szExePath, &dwDummy);
272 if (cbVersion)
273 {
274 BYTE *pVersionInfo = (BYTE *)RTMemTmpAllocZ(cbVersion);
275 if (pVersionInfo)
276 {
277 if (GetFileVersionInfo(pModule->Info.szExePath, 0, cbVersion, pVersionInfo))
278 vgsvcPageSharingRegisterModuleInner(pModule, fValidateMemory, pVersionInfo);
279 else
280 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: GetFileVersionInfo failed with %d\n", GetLastError());
281 RTMemTmpFree(pVersionInfo);
282 }
283 }
284 else
285 VGSvcVerbose(3, "vgsvcPageSharingRegisterModule: GetFileVersionInfoSize failed with %d\n", GetLastError());
[28432]286}
287
[58029]288
[28432]289/**
290 * Inspect all loaded modules for the specified process
[58089]291 *
[28432]292 * @param dwProcessId Process id
[58089]293 * @param ppNewTree The module tree we're assembling from modules found
294 * in this process. Modules found are moved from
295 * g_pKnownModuleTree or created new.
[28432]296 */
[58029]297static void vgsvcPageSharingInspectModules(DWORD dwProcessId, PAVLPVNODECORE *ppNewTree)
[28432]298{
299 /* Get a list of all the modules in this process. */
[58029]300 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE /* no child process handle inheritance */, dwProcessId);
[28432]301 if (hProcess == NULL)
[29351]302 {
[58029]303 VGSvcVerbose(3, "vgsvcPageSharingInspectModules: OpenProcess %x failed with %d\n", dwProcessId, GetLastError());
[28432]304 return;
[29351]305 }
[28432]306
[70171]307 HANDLE hSnapshot = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
[28432]308 if (hSnapshot == INVALID_HANDLE_VALUE)
309 {
[58029]310 VGSvcVerbose(3, "vgsvcPageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
[28432]311 CloseHandle(hProcess);
312 return;
313 }
314
[58029]315 VGSvcVerbose(3, "vgsvcPageSharingInspectModules\n");
[29365]316
[28432]317 MODULEENTRY32 ModuleInfo;
318 BOOL bRet;
319
320 ModuleInfo.dwSize = sizeof(ModuleInfo);
[70171]321 bRet = g_pfnModule32First(hSnapshot, &ModuleInfo);
[28432]322 do
323 {
[40054]324 /** @todo when changing this make sure VBoxService.exe is excluded! */
[29473]325 char *pszDot = strrchr(ModuleInfo.szModule, '.');
[30013]326 if ( pszDot
[29473]327 && (pszDot[1] == 'e' || pszDot[1] == 'E'))
328 continue; /* ignore executables for now. */
329
[28432]330 /* Found it before? */
331 PAVLPVNODECORE pRec = RTAvlPVGet(ppNewTree, ModuleInfo.modBaseAddr);
332 if (!pRec)
333 {
[30668]334 pRec = RTAvlPVRemove(&g_pKnownModuleTree, ModuleInfo.modBaseAddr);
[28432]335 if (!pRec)
336 {
337 /* New module; register it. */
[58029]338 PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)RTMemAllocZ(sizeof(*pModule));
[28432]339 Assert(pModule);
340 if (!pModule)
341 break;
342
343 pModule->Info = ModuleInfo;
344 pModule->Core.Key = ModuleInfo.modBaseAddr;
345 pModule->hModule = LoadLibraryEx(ModuleInfo.szExePath, 0, DONT_RESOLVE_DLL_REFERENCES);
[29420]346 if (pModule->hModule)
[58029]347 vgsvcPageSharingRegisterModule(pModule, true /* validate pages */);
[28432]348
[58029]349 VGSvcVerbose(3, "\n\n MODULE NAME: %s", ModuleInfo.szModule );
350 VGSvcVerbose(3, "\n executable = %s", ModuleInfo.szExePath );
351 VGSvcVerbose(3, "\n process ID = 0x%08X", ModuleInfo.th32ProcessID );
[62851]352 VGSvcVerbose(3, "\n base address = %#010p", (uintptr_t) ModuleInfo.modBaseAddr );
[58029]353 VGSvcVerbose(3, "\n base size = %d", ModuleInfo.modBaseSize );
[29952]354
[28432]355 pRec = &pModule->Core;
356 }
357 bool ret = RTAvlPVInsert(ppNewTree, pRec);
358 Assert(ret); NOREF(ret);
359 }
[70171]360 } while (g_pfnModule32Next(hSnapshot, &ModuleInfo));
[28432]361
[29355]362 CloseHandle(hSnapshot);
[28432]363 CloseHandle(hProcess);
364}
365
[58029]366
[28432]367/**
368 * Inspect all running processes for executables and dlls that might be worth sharing
369 * with other VMs.
370 *
371 */
[58029]372static void vgsvcPageSharingInspectGuest(void)
[28432]373{
[70171]374 VGSvcVerbose(3, "vgsvcPageSharingInspectGuest\n");
[28432]375 PAVLPVNODECORE pNewTree = NULL;
376
[70171]377 /*
378 * Check loaded modules for all running processes.
379 */
380 if ( g_pfnProcess32First
381 && g_pfnProcess32Next
382 && g_pfnModule32First
383 && g_pfnModule32Next
384 && g_pfnCreateToolhelp32Snapshot)
[29351]385 {
[70171]386 HANDLE hSnapshot = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
387 if (hSnapshot == INVALID_HANDLE_VALUE)
388 {
389 VGSvcVerbose(3, "vgsvcPageSharingInspectGuest: CreateToolhelp32Snapshot failed with %d\n", GetLastError());
390 return;
391 }
[28432]392
[70171]393 DWORD const dwProcessId = GetCurrentProcessId();
[28432]394
[70171]395 PROCESSENTRY32 ProcessInfo;
396 ProcessInfo.dwSize = sizeof(ProcessInfo);
397 g_pfnProcess32First(hSnapshot, &ProcessInfo);
[28432]398
[70171]399 do
400 {
401 /* Skip our own process. */
402 if (ProcessInfo.th32ProcessID != dwProcessId)
403 vgsvcPageSharingInspectModules(ProcessInfo.th32ProcessID, &pNewTree);
404 }
405 while (g_pfnProcess32Next(hSnapshot, &ProcessInfo));
406
407 CloseHandle(hSnapshot);
[28432]408 }
409
[70171]410 /*
411 * Check all loaded kernel modules.
412 */
[46593]413 if (g_pfnZwQuerySystemInformation)
[29955]414 {
[29956]415 ULONG cbBuffer = 0;
[29955]416 PVOID pBuffer = NULL;
417 PRTL_PROCESS_MODULES pSystemModules;
[30013]418
[46593]419 NTSTATUS ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer);
[29959]420 if (!cbBuffer)
[29955]421 {
[58029]422 VGSvcVerbose(1, "ZwQuerySystemInformation returned length 0\n");
[29955]423 goto skipkernelmodules;
424 }
[30013]425
[29955]426 pBuffer = RTMemAllocZ(cbBuffer);
427 if (!pBuffer)
428 goto skipkernelmodules;
[29952]429
[46593]430 ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer);
[29955]431 if (ret != STATUS_SUCCESS)
432 {
[58029]433 VGSvcVerbose(1, "ZwQuerySystemInformation returned %x (1)\n", ret);
[29955]434 goto skipkernelmodules;
435 }
[30013]436
[29955]437 pSystemModules = (PRTL_PROCESS_MODULES)pBuffer;
438 for (unsigned i = 0; i < pSystemModules->NumberOfModules; i++)
[29952]439 {
[58029]440 VGSvcVerbose(4, "\n\n KERNEL MODULE NAME: %s", pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName] );
441 VGSvcVerbose(4, "\n executable = %s", pSystemModules->Modules[i].FullPathName );
442 VGSvcVerbose(4, "\n flags = 0x%08X\n", pSystemModules->Modules[i].Flags);
[30034]443
[29959]444 /* User-mode modules seem to have no flags set; skip them as we detected them above. */
[29962]445 if (pSystemModules->Modules[i].Flags == 0)
[29959]446 continue;
447
[29955]448 /* Found it before? */
449 PAVLPVNODECORE pRec = RTAvlPVGet(&pNewTree, pSystemModules->Modules[i].ImageBase);
[29952]450 if (!pRec)
451 {
[30668]452 pRec = RTAvlPVRemove(&g_pKnownModuleTree, pSystemModules->Modules[i].ImageBase);
[29955]453 if (!pRec)
454 {
455 /* New module; register it. */
[58029]456 char szFullFilePath[512];
457 PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)RTMemAllocZ(sizeof(*pModule));
[29955]458 Assert(pModule);
459 if (!pModule)
460 break;
[29952]461
[95851]462/** @todo FullPathName not an UTF-8 string is! An ANSI string it is
463 * according to the SYSTEM locale. Best use RtlAnsiStringToUnicodeString to
464 * convert to UTF-16. */
[70171]465 strcpy(pModule->Info.szModule,
466 (const char *)&pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName]);
[29955]467 GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
[29952]468
[29955]469 /* skip \Systemroot\system32 */
[70171]470 char *lpPath = strchr((char *)&pSystemModules->Modules[i].FullPathName[1], '\\');
[29955]471 if (!lpPath)
472 {
[30036]473 /* Seen just file names in XP; try to locate the file in the system32 and system32\drivers directories. */
[95851]474 RTStrCat(szFullFilePath, sizeof(szFullFilePath), "\\");
475 RTStrCat(szFullFilePath, sizeof(szFullFilePath), (const char *)pSystemModules->Modules[i].FullPathName);
[58029]476 VGSvcVerbose(3, "Unexpected kernel module name try %s\n", szFullFilePath);
[30036]477 if (RTFileExists(szFullFilePath) == false)
478 {
479 GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath));
[95851]480 RTStrCat(szFullFilePath, sizeof(szFullFilePath), "\\drivers\\");
481 RTStrCat(szFullFilePath, sizeof(szFullFilePath), (const char *)pSystemModules->Modules[i].FullPathName);
[58029]482 VGSvcVerbose(3, "Unexpected kernel module name try %s\n", szFullFilePath);
[30036]483 if (RTFileExists(szFullFilePath) == false)
484 {
[58029]485 VGSvcVerbose(1, "Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName);
[30036]486 RTMemFree(pModule);
487 continue;
488 }
489 }
[29955]490 }
[30036]491 else
492 {
[70171]493 lpPath = strchr(lpPath + 1, '\\');
[30036]494 if (!lpPath)
495 {
[58029]496 VGSvcVerbose(1, "Unexpected kernel module name %s (2)\n", pSystemModules->Modules[i].FullPathName);
[30036]497 RTMemFree(pModule);
498 continue;
499 }
[29952]500
[95851]501 RTStrCat(szFullFilePath, sizeof(szFullFilePath), lpPath);
[29955]502 }
[29952]503
[29955]504 strcpy(pModule->Info.szExePath, szFullFilePath);
505 pModule->Info.modBaseAddr = (BYTE *)pSystemModules->Modules[i].ImageBase;
506 pModule->Info.modBaseSize = pSystemModules->Modules[i].ImageSize;
[29952]507
[29955]508 pModule->Core.Key = pSystemModules->Modules[i].ImageBase;
[58029]509 vgsvcPageSharingRegisterModule(pModule, false /* don't check memory pages */);
[29952]510
[58029]511 VGSvcVerbose(3, "\n\n KERNEL MODULE NAME: %s", pModule->Info.szModule );
512 VGSvcVerbose(3, "\n executable = %s", pModule->Info.szExePath );
[62851]513 VGSvcVerbose(3, "\n base address = %#010p", (uintptr_t)pModule->Info.modBaseAddr );
[58029]514 VGSvcVerbose(3, "\n flags = 0x%08X", pSystemModules->Modules[i].Flags);
515 VGSvcVerbose(3, "\n base size = %d", pModule->Info.modBaseSize );
[29952]516
[29955]517 pRec = &pModule->Core;
518 }
[83822]519 bool fRet = RTAvlPVInsert(&pNewTree, pRec);
520 Assert(fRet); NOREF(fRet);
[29952]521 }
522 }
[29955]523skipkernelmodules:
524 if (pBuffer)
525 RTMemFree(pBuffer);
[29952]526 }
527
[28432]528 /* Delete leftover modules in the old tree. */
[58029]529 RTAvlPVDestroy(&g_pKnownModuleTree, vgsvcPageSharingEmptyTreeCallback, NULL);
[28432]530
[29424]531 /* Check all registered modules. */
[30665]532 VbglR3CheckSharedModules();
[30668]533
[30665]534 /* Activate new module tree. */
[30668]535 g_pKnownModuleTree = pNewTree;
[28432]536}
537
[58029]538
[28432]539/**
540 * RTAvlPVDestroy callback.
541 */
[58029]542static DECLCALLBACK(int) vgsvcPageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *pvUser)
[28432]543{
[58029]544 PVGSVCPGSHKNOWNMOD pModule = (PVGSVCPGSHKNOWNMOD)pNode;
[30646]545 bool *pfUnregister = (bool *)pvUser;
[28432]546
[58029]547 VGSvcVerbose(3, "vgsvcPageSharingEmptyTreeCallback %s %s\n", pModule->Info.szModule, pModule->szFileVersion);
[29351]548
[30646]549 /* Dereference module in the hypervisor. */
[40054]550 if ( !pfUnregister
551 || *pfUnregister)
[30646]552 {
[40054]553 int rc = VbglR3UnregisterSharedModule(pModule->Info.szModule, pModule->szFileVersion,
554 (uintptr_t)pModule->Info.modBaseAddr, pModule->Info.modBaseSize);
[30646]555 AssertRC(rc);
556 }
[28432]557
558 if (pModule->hModule)
559 FreeLibrary(pModule->hModule);
560 RTMemFree(pNode);
561 return 0;
562}
563
564
[70171]565#else /* !RT_OS_WINDOWS */
[58029]566
567static void vgsvcPageSharingInspectGuest(void)
[28432]568{
[56282]569 /** @todo other platforms */
[28432]570}
[58029]571
[70171]572#endif /* !RT_OS_WINDOWS */
[28432]573
[58029]574/** @interface_method_impl{VBOXSERVICE,pfnInit} */
575static DECLCALLBACK(int) vgsvcPageSharingInit(void)
[28432]576{
[58029]577 VGSvcVerbose(3, "vgsvcPageSharingInit\n");
[28432]578
579 int rc = RTSemEventMultiCreate(&g_PageSharingEvent);
580 AssertRCReturn(rc, rc);
581
[70171]582#ifdef RT_OS_WINDOWS
[46593]583 rc = VbglR3GetSessionId(&g_idSession);
[38411]584 if (RT_FAILURE(rc))
585 {
586 if (rc == VERR_IO_GEN_FAILURE)
[58029]587 VGSvcVerbose(0, "PageSharing: Page sharing support is not available by the host\n");
[38411]588 else
[58029]589 VGSvcError("vgsvcPageSharingInit: Failed with rc=%Rrc\n", rc);
[38411]590
591 rc = VERR_SERVICE_DISABLED;
592
593 RTSemEventMultiDestroy(g_PageSharingEvent);
594 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
595
596 }
[30671]597#endif
[30668]598
[38411]599 return rc;
[28432]600}
601
[58029]602
603/**
604 * @interface_method_impl{VBOXSERVICE,pfnWorker}
605 */
606static DECLCALLBACK(int) vgsvcPageSharingWorker(bool volatile *pfShutdown)
[28432]607{
608 /*
609 * Tell the control thread that it can continue
610 * spawning services.
611 */
612 RTThreadUserSignal(RTThreadSelf());
613
[29938]614 /*
[30560]615 * Now enter the loop retrieving runtime data continuously.
[30013]616 */
[30560]617 for (;;)
618 {
[58029]619 bool fEnabled = VbglR3PageSharingIsEnabled();
620 VGSvcVerbose(3, "vgsvcPageSharingWorker: enabled=%d\n", fEnabled);
[30668]621
[30755]622 if (fEnabled)
[58029]623 vgsvcPageSharingInspectGuest();
[30560]624
625 /*
626 * Block for a minute.
627 *
628 * The event semaphore takes care of ignoring interruptions and it
629 * allows us to implement service wakeup later.
630 */
631 if (*pfShutdown)
632 break;
633 int rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
634 if (*pfShutdown)
635 break;
636 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
637 {
[58029]638 VGSvcError("vgsvcPageSharingWorker: RTSemEventMultiWait failed; rc=%Rrc\n", rc);
[30560]639 break;
640 }
[70171]641#ifdef RT_OS_WINDOWS
[35036]642 uint64_t idNewSession = g_idSession;
[30668]643 rc = VbglR3GetSessionId(&idNewSession);
644 AssertRC(rc);
645
646 if (idNewSession != g_idSession)
647 {
[30754]648 bool fUnregister = false;
[30668]649
[58029]650 VGSvcVerbose(3, "vgsvcPageSharingWorker: VM was restored!!\n");
[30754]651 /* The VM was restored, so reregister all modules the next time. */
[58029]652 RTAvlPVDestroy(&g_pKnownModuleTree, vgsvcPageSharingEmptyTreeCallback, &fUnregister);
[30669]653 g_pKnownModuleTree = NULL;
654
655 g_idSession = idNewSession;
[30668]656 }
[30672]657#endif
[29936]658 }
659
[30560]660 RTSemEventMultiDestroy(g_PageSharingEvent);
661 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
662
[58029]663 VGSvcVerbose(3, "vgsvcPageSharingWorker: finished thread\n");
[30560]664 return 0;
665}
666
667#ifdef RT_OS_WINDOWS
[30601]668
669/**
[56282]670 * This gets control when VBoxService is launched with "pagefusion" by
[58029]671 * vgsvcPageSharingWorkerProcess().
[30601]672 *
673 * @returns RTEXITCODE_SUCCESS.
674 *
675 * @remarks It won't normally return since the parent drops the shutdown hint
676 * via RTProcTerminate().
677 */
[58029]678RTEXITCODE VGSvcPageSharingWorkerChild(void)
[30560]679{
[58029]680 VGSvcVerbose(3, "vgsvcPageSharingInitFork\n");
[30560]681
682 bool fShutdown = false;
[58029]683 vgsvcPageSharingInit();
684 vgsvcPageSharingWorker(&fShutdown);
[30601]685
686 return RTEXITCODE_SUCCESS;
[30560]687}
688
[58029]689
690/**
691 * @interface_method_impl{VBOXSERVICE,pfnWorker}
692 */
693static DECLCALLBACK(int) vgsvcPageSharingWorkerProcess(bool volatile *pfShutdown)
[30560]694{
695 RTPROCESS hProcess = NIL_RTPROCESS;
696 int rc;
697
[28432]698 /*
[30560]699 * Tell the control thread that it can continue
700 * spawning services.
701 */
702 RTThreadUserSignal(RTThreadSelf());
703
704 /*
[28432]705 * Now enter the loop retrieving runtime data continuously.
706 */
707 for (;;)
708 {
[58029]709 bool fEnabled = VbglR3PageSharingIsEnabled();
710 VGSvcVerbose(3, "vgsvcPageSharingWorkerProcess: enabled=%d\n", fEnabled);
[28434]711
[30957]712 /*
713 * Start a 2nd VBoxService process to deal with page fusion as we do
714 * not wish to dummy load dlls into this process. (First load with
715 * DONT_RESOLVE_DLL_REFERENCES, 2nd normal -> dll init routines not called!)
716 */
[30754]717 if ( fEnabled
[30560]718 && hProcess == NIL_RTPROCESS)
719 {
720 char szExeName[256];
[33806]721 char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName));
[30560]722 if (pszExeName)
723 {
[30957]724 char const *papszArgs[3];
[30960]725 papszArgs[0] = pszExeName;
[44863]726 papszArgs[1] = "pagefusion";
[30960]727 papszArgs[2] = NULL;
[30957]728 rc = RTProcCreate(pszExeName, papszArgs, RTENV_DEFAULT, 0 /* normal child */, &hProcess);
729 if (RT_FAILURE(rc))
[58029]730 VGSvcError("vgsvcPageSharingWorkerProcess: RTProcCreate %s failed; rc=%Rrc\n", pszExeName, rc);
[30560]731 }
732 }
733
[28432]734 /*
735 * Block for a minute.
736 *
737 * The event semaphore takes care of ignoring interruptions and it
738 * allows us to implement service wakeup later.
739 */
740 if (*pfShutdown)
741 break;
[29936]742 rc = RTSemEventMultiWait(g_PageSharingEvent, 60000);
[28432]743 if (*pfShutdown)
744 break;
745 if (rc != VERR_TIMEOUT && RT_FAILURE(rc))
746 {
[58029]747 VGSvcError("vgsvcPageSharingWorkerProcess: RTSemEventMultiWait failed; rc=%Rrc\n", rc);
[28432]748 break;
749 }
750 }
751
[30754]752 if (hProcess != NIL_RTPROCESS)
[30560]753 RTProcTerminate(hProcess);
754
[58029]755 VGSvcVerbose(3, "vgsvcPageSharingWorkerProcess: finished thread\n");
[28432]756 return 0;
757}
758
[30601]759#endif /* RT_OS_WINDOWS */
[30560]760
[58029]761/**
762 * @interface_method_impl{VBOXSERVICE,pfnStop}
763 */
764static DECLCALLBACK(void) vgsvcPageSharingStop(void)
[28432]765{
766 RTSemEventMultiSignal(g_PageSharingEvent);
767}
768
769
770/**
[58182]771 * @interface_method_impl{VBOXSERVICE,pfnTerm}
772 */
773static DECLCALLBACK(void) vgsvcPageSharingTerm(void)
774{
775 if (g_PageSharingEvent != NIL_RTSEMEVENTMULTI)
776 {
777 RTSemEventMultiDestroy(g_PageSharingEvent);
778 g_PageSharingEvent = NIL_RTSEMEVENTMULTI;
779 }
780}
781
782
783/**
[28432]784 * The 'pagesharing' service description.
785 */
786VBOXSERVICE g_PageSharing =
787{
788 /* pszName. */
789 "pagesharing",
790 /* pszDescription. */
791 "Page Sharing",
792 /* pszUsage. */
793 NULL,
794 /* pszOptions. */
795 NULL,
796 /* methods */
[58029]797 VGSvcDefaultPreInit,
798 VGSvcDefaultOption,
799 vgsvcPageSharingInit,
[30560]800#ifdef RT_OS_WINDOWS
[58029]801 vgsvcPageSharingWorkerProcess,
[30560]802#else
[58029]803 vgsvcPageSharingWorker,
[30560]804#endif
[58029]805 vgsvcPageSharingStop,
[58182]806 vgsvcPageSharingTerm
[28432]807};
[58029]808
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use