diff options
Diffstat (limited to '')
-rw-r--r-- | src/VBox/Additions/common/testcase/Makefile.kmk | 53 | ||||
-rwxr-xr-x | src/VBox/Additions/common/testcase/led-lights.sh | 276 | ||||
-rw-r--r-- | src/VBox/Additions/common/testcase/tstPageFusion.cpp | 389 |
3 files changed, 718 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/testcase/Makefile.kmk b/src/VBox/Additions/common/testcase/Makefile.kmk new file mode 100644 index 00000000..f18504b2 --- /dev/null +++ b/src/VBox/Additions/common/testcase/Makefile.kmk @@ -0,0 +1,53 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the Cross Platform Guest Addition test cases. +# + +# +# Copyright (C) 2007-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# SPDX-License-Identifier: GPL-3.0-only +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +# +# Target lists. +# +PROGRAMS += tstPageFusion + +# +# tstPageFusion +# +tstPageFusion_TEMPLATE = VBoxGuestR3Exe +tstPageFusion_DEFS.win += _WIN32_WINNT=0x0501 +tstPageFusion_SOURCES = \ + tstPageFusion.cpp + +# +# Install the LED test script to bin. +# +INSTALLS += lights-test-script +lights-test-script_INST = $(INST_BIN) +lights-test-script_MODE = a+rx,u+w +lights-test-script_SOURCES = led-lights.sh + +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/Additions/common/testcase/led-lights.sh b/src/VBox/Additions/common/testcase/led-lights.sh new file mode 100755 index 00000000..6d7e6504 --- /dev/null +++ b/src/VBox/Additions/common/testcase/led-lights.sh @@ -0,0 +1,276 @@ +#!/bin/bash +# $Id: led-lights.sh $ +## @file +# VirtualBox guest LED demonstration test +# + +# +# Copyright (C) 2021-2023 Oracle and/or its affiliates. +# +# This file is part of VirtualBox base platform packages, as +# available from https://www.virtualbox.org. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, in version 3 of the +# License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <https://www.gnu.org/licenses>. +# +# SPDX-License-Identifier: GPL-3.0-only +# + +# +# Usage: +# led-lights.sh [-a | -r] +# + +# +# Test script to twiddle the console LEDs of a VirtualBox VM. +# +# This is not an automated test, just something for humans to look +# at, to convince themselves that the VM console LEDs are working. +# By default it cycles through the LED types in a specific order. +# +# '-a' twiddles all possible LEDs at the same time +# '-r' reverses the default order +# +# For instance, run the script in 2 VMs at once, one with '-r'. +# +# LEDs are not expected to track perfectly, as other OS activities +# will light them (and buffer cache effects can delay things). Just +# make sure that all the tested ones (hard disk, optical, USB storage, +# floppy, shared folders, net) are working. Expected activity: +# +# - Disk & optical devices show solid 'read' +# - Virtual USB disk & optical devices show 'write' on the USB LED +# - Floppy devices and shared folders alternate 'read/write' +# - Net blinks 'write' +# +# Pre-VM setup: +# +# Download or locate a bootable Linux ISO able to be used 'live'. +# Make a tarball of this script + extra junk: +# +# $ dd if=/dev/zero of=junk bs=100k count=1 +# $ tar cf floppy.img led-lights.sh junk +# +# NOTE: floppy.img must be >= 20KiB or you will get I/O errors! +# +# VM setup: +# +# New VM; type: Linux (subtype to match ISO); create default HD. +# VM Settings: +# System > raise base memory to 4GiB +# Storage > insert 'Live bootable' Linux ISO image; +# turn on 'Live CD/DVD' +# Storage > add floppy controller (i82078); insert floppy.img +# Storage > add USB controller; insert USB HD & USB CD +# System > move Optical before Floppy in boot order +# Shared Folders > set up one or more Shared Folders if desired +# (they should be permanent, auto-mount, +# writable, with at least 10MB free space) +# +# Boot the VM. Open a shell, become root, optionally install +# VirtualBox Guest Utilities to access Shared Folders, then extract +# and run this script: +# +# $ sudo bash +# # yum install virtualbox-guest-utils # for Shared Folders +# # tar xf /dev/fd0 led-lights.sh +# # ./led-lights.sh [-a | -r] + +if [ ! -w / ]; then + echo "Must be run as root!" 1>&2 + exit 1 +fi + +all_all=false +reverse=false + +if [ "x$1" = "x-a" ]; then + all_all=true +fi + +if [ "x$1" = "x-r" ]; then + reverse=true +fi + +# Copy binaries to RAM tmpfs to avoid CD I/O after cache purges +MYTMP=/tmp/led-lights.$$ +mkdir $MYTMP +for bin in $(which dd sleep sync); do + case $bin in + /*) + cp -p $bin $MYTMP + ;; + esac +done +export MYTMP PATH=$MYTMP:$PATH + +set -o monitor + +# Make device reads keep hitting the 'hardware' +# even if the whole medium fits in cache... +drop_cache() +{ + echo 1 >/proc/sys/vm/drop_caches +} + +activate() +{ + kill -CONT -$1 2>/dev/null +} + +suppress() +{ + $all_all || kill -STOP -$1 2>/dev/null +} + +declare -a pids pidnames +cpids=0 + +twiddle() +{ + let ++cpids + new_pid=$! + pidname=$1 + pids[$cpids]=$new_pid + pidnames[$cpids]=$pidname + suppress $new_pid +} + +hide_stderr() +{ + exec 3>&2 2>/dev/null +} + +show_stderr() +{ + exec 2>&3 3>&- +} + +bail() +{ + hide_stderr + for pid in ${pids[*]}; do + activate $pid + kill -TERM -$pid + kill -TERM $pid + done 2>/dev/null + rm -rf $MYTMP + kill $$ +} + +trap "bail" INT + +drives() +{ + echo $( + awk '$NF ~/^('$1')$/ { print $NF }' /proc/partitions + ) +} + +# Prevent job control 'stopped' msgs during twiddler startup +hide_stderr + +# Hard disks +for hdd in $(drives '[sh]d.'); do + while :; do + dd if=/dev/$hdd of=/dev/null + drop_cache + done 2>/dev/null & + twiddle disk:$hdd +done + +# Optical drives +for odd in $(drives 'sr.|scd.'); do + while :; do + dd if=/dev/$odd of=/dev/null + drop_cache + done 2>/dev/null & + twiddle optical:$odd +done + +# Floppy drives +for fdd in $(drives 'fd.'); do + while :; do + dd if=/dev/$fdd of=$MYTMP/$fdd bs=1k count=20 + dd of=/dev/$fdd if=$MYTMP/$fdd bs=1k count=20 + done 2>/dev/null & + twiddle floppy:$fdd +done + +# Shared folders +if ! lsmod | grep -q vboxsf; then + echo + echo "Note: to test the Shared Folders LED, install this" + echo "distro's VirtualBox Guest Utilities package, e.g.:" + echo + echo " # yum install virtualbox-guest-utils (Red Hat family)" + echo " # apt install virtualbox-guest-utils (Debian family)" + echo +fi >&3 # original stderr +for shf in $(mount -t vboxsf | awk '{ print $3 }'); do + while :; do + dd if=/dev/urandom of=$shf/tmp.led-lights.$$ bs=100k count=100 + for rep in $(seq 1 10); do + drop_cache + dd of=/dev/zero if=$shf/tmp.led-lights.$$ bs=100k count=100 + done + sync + rm -f $shf/tmp.led-lights.$$ + done >/dev/null 2>&1 & + twiddle sharedfs:$shf +done + +# Network +ping -i.2 1.2.3.4 >/dev/null & +twiddle net + +# Untested LED: Graphics3D -- add some day? + +sleep 0.1 +show_stderr + +if $reverse; then + seq=$(seq $cpids -1 1) +else + seq=$(seq 1 $cpids) +fi + +show_intr() +{ + intr=$(stty -a | sed -n '/intr/ { s/.*intr *=* *//; s/[ ;].*//p }') + echo + echo "[ Hit $intr to stop ]" + echo +} + +if $all_all; then + printf "%s ...\n" ${pidnames[*]} + show_intr + wait +else + CEOL=$(tput el) + show_intr + while :; do + for pidx in $seq; do + pid=${pids[$pidx]} + pidname=${pidnames[$pidx]} + echo -e -n "$pidname$CEOL\r" + shift + activate $pid + sleep 2 + suppress $pid + sync + sleep .5 + done + done +fi diff --git a/src/VBox/Additions/common/testcase/tstPageFusion.cpp b/src/VBox/Additions/common/testcase/tstPageFusion.cpp new file mode 100644 index 00000000..264d887a --- /dev/null +++ b/src/VBox/Additions/common/testcase/tstPageFusion.cpp @@ -0,0 +1,389 @@ +/* $Id: tstPageFusion.cpp $ */ +/** @file + * VBoxService - Guest page sharing testcase + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/mem.h> +#include <iprt/messages.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/initterm.h> +#include <VBox/VBoxGuestLib.h> +#include <iprt/x86.h> +#include <stdio.h> + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ + +#ifdef RT_OS_WINDOWS +#include <iprt/win/windows.h> +#include <process.h> /* Needed for file version information. */ +#include <tlhelp32.h> +#include <psapi.h> +#include <winternl.h> + +#define SystemModuleInformation 11 + +typedef struct _RTL_PROCESS_MODULE_INFORMATION +{ + ULONG Section; + PVOID MappedBase; + PVOID ImageBase; + ULONG ImageSize; + ULONG Flags; + USHORT LoadOrderIndex; + USHORT InitOrderIndex; + USHORT LoadCount; + USHORT OffsetToFileName; + CHAR FullPathName[256]; +} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION; + +typedef struct _RTL_PROCESS_MODULES +{ + ULONG NumberOfModules; + RTL_PROCESS_MODULE_INFORMATION Modules[1]; +} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; + +typedef NTSTATUS (WINAPI *PFNZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG); +static PFNZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL; +static HMODULE hNtdll = 0; + +#define PAGE_STATE_INVALID 0 +#define PAGE_STATE_SHARED 1 +#define PAGE_STATE_READ_WRITE 2 +#define PAGE_STATE_READ_ONLY 3 +#define PAGE_STATE_NOT_PRESENT 4 + +/* Page counters. */ +static unsigned cNotPresentPages = 0; +static unsigned cWritablePages = 0; +static unsigned cSharedPages = 0; +static unsigned cPrivatePages = 0; + +/** + * Registers a new module with the VMM + * @param pModule Module ptr + */ +void VBoxServicePageSharingCheckModule(MODULEENTRY32 *pModule) +{ + DWORD dwModuleSize = pModule->modBaseSize; + BYTE *pBaseAddress = pModule->modBaseAddr; + bool fFirstLine = true; + unsigned uPageState, uLastPageState; + bool fLastWritable = false; + BYTE *pLastBaseAddress = pBaseAddress; + + uPageState = uLastPageState = PAGE_STATE_INVALID; + + printf("Check module %s base %p size %x\n", pModule->szModule, pBaseAddress, dwModuleSize); + do + { + bool fShared; + uint64_t uPageFlags; + +#ifdef RT_ARCH_X86 + int rc = VbglR3PageIsShared((uint32_t)pLastBaseAddress, &fShared, &uPageFlags); +#else + int rc = VbglR3PageIsShared((RTGCPTR)pLastBaseAddress, &fShared, &uPageFlags); +#endif + if (RT_FAILURE(rc)) + printf("VbglR3PageIsShared %p failed with %d\n", pLastBaseAddress, rc); + + if (RT_SUCCESS(rc)) + { + if (uPageFlags & X86_PTE_P) + { + if (uPageFlags & X86_PTE_RW) + { + cWritablePages++; + uPageState = PAGE_STATE_READ_WRITE; + } + else + if (fShared) + { + cSharedPages++; + uPageState = PAGE_STATE_SHARED; + } + else + { + cPrivatePages++; + uPageState = PAGE_STATE_READ_ONLY; + } + } + else + { + cNotPresentPages++; + uPageState = PAGE_STATE_NOT_PRESENT; + } + + if ( !fFirstLine + && uPageState != uLastPageState) + { + printf("0x%p\n", pLastBaseAddress + 0xfff); + } + + if (uPageState != uLastPageState) + { + switch (uPageState) + { + case PAGE_STATE_READ_WRITE: + printf("%s RW 0x%p - ", pModule->szModule, pBaseAddress); + break; + case PAGE_STATE_SHARED: + printf("%s SHARED 0x%p - ", pModule->szModule, pBaseAddress); + break; + case PAGE_STATE_READ_ONLY: + printf("%s PRIV 0x%p - ", pModule->szModule, pBaseAddress); + break; + case PAGE_STATE_NOT_PRESENT: + printf("%s NP 0x%p - ", pModule->szModule, pBaseAddress); + break; + } + + fFirstLine = false; + } + uLastPageState = uPageState; + } + else + if (!fFirstLine) + { + printf("0x%p\n", pLastBaseAddress + 0xfff); + fFirstLine = true; + } + + if (dwModuleSize > PAGE_SIZE) + dwModuleSize -= PAGE_SIZE; + else + dwModuleSize = 0; + + pLastBaseAddress = pBaseAddress; + pBaseAddress += PAGE_SIZE; + } + while (dwModuleSize); + + printf("0x%p\n", pLastBaseAddress + 0xfff); + return; +} + +/** + * Inspect all loaded modules for the specified process + * @param dwProcessId Process id + */ +void VBoxServicePageSharingInspectModules(DWORD dwProcessId) +{ + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId); + if (hSnapshot == INVALID_HANDLE_VALUE) + { + printf("VBoxServicePageSharingInspectModules: CreateToolhelp32Snapshot failed with %d\n", GetLastError()); + return; + } + + printf("VBoxServicePageSharingInspectModules\n"); + + MODULEENTRY32 ModuleInfo; + BOOL bRet; + + ModuleInfo.dwSize = sizeof(ModuleInfo); + bRet = Module32First(hSnapshot, &ModuleInfo); + do + { + /** @todo when changing this make sure VBoxService.exe is excluded! */ + char *pszDot = strrchr(ModuleInfo.szModule, '.'); + if ( pszDot + && (pszDot[1] == 'e' || pszDot[1] == 'E')) + continue; /* ignore executables for now. */ + + VBoxServicePageSharingCheckModule(&ModuleInfo); + } + while (Module32Next(hSnapshot, &ModuleInfo)); + + CloseHandle(hSnapshot); +} + +/** + * Inspect all running processes for executables and dlls that might be worth sharing + * with other VMs. + * + */ +void VBoxServicePageSharingInspectGuest() +{ + VBoxServicePageSharingInspectModules(GetCurrentProcessId()); + + printf("\n\nUSER RESULTS\n"); + printf("cNotPresentPages = %d\n", cNotPresentPages); + printf("cWritablePages = %d\n", cWritablePages); + printf("cPrivatePages = %d\n", cPrivatePages); + printf("cSharedPages = %d\n", cSharedPages); + + cNotPresentPages = 0; + cWritablePages = 0; + cPrivatePages = 0; + cSharedPages = 0; + + /* Check all loaded kernel modules. */ + if (ZwQuerySystemInformation) + { + ULONG cbBuffer = 0; + PVOID pBuffer = NULL; + PRTL_PROCESS_MODULES pSystemModules; + + NTSTATUS ret = ZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer); + if (!cbBuffer) + { + printf("ZwQuerySystemInformation returned length 0\n"); + goto skipkernelmodules; + } + + pBuffer = RTMemAllocZ(cbBuffer); + if (!pBuffer) + goto skipkernelmodules; + + ret = ZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer); + if (ret != 0) + { + printf("ZwQuerySystemInformation returned %x (1)\n", ret); + goto skipkernelmodules; + } + + pSystemModules = (PRTL_PROCESS_MODULES)pBuffer; + for (unsigned i = 0; i < pSystemModules->NumberOfModules; i++) + { + /* User-mode modules seem to have no flags set; skip them as we detected them above. */ + if (pSystemModules->Modules[i].Flags == 0) + continue; + + /* New module; register it. */ + char szFullFilePath[512]; + MODULEENTRY32 ModuleInfo; + + strcpy(ModuleInfo.szModule, &pSystemModules->Modules[i].FullPathName[pSystemModules->Modules[i].OffsetToFileName]); + GetSystemDirectoryA(szFullFilePath, sizeof(szFullFilePath)); + + /* skip \Systemroot\system32 */ + char *lpPath = strchr(&pSystemModules->Modules[i].FullPathName[1], '\\'); + if (!lpPath) + { + printf("Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName); + break; + } + + lpPath = strchr(lpPath+1, '\\'); + if (!lpPath) + { + printf("Unexpected kernel module name %s\n", pSystemModules->Modules[i].FullPathName); + break; + } + + strcat(szFullFilePath, lpPath); + strcpy(ModuleInfo.szExePath, szFullFilePath); + ModuleInfo.modBaseAddr = (BYTE *)pSystemModules->Modules[i].ImageBase; + ModuleInfo.modBaseSize = pSystemModules->Modules[i].ImageSize; + + VBoxServicePageSharingCheckModule(&ModuleInfo); + } +skipkernelmodules: + if (pBuffer) + RTMemFree(pBuffer); + } + printf("\n\nKERNEL RESULTS\n"); + printf("cNotPresentPages = %d\n", cNotPresentPages); + printf("cWritablePages = %d\n", cWritablePages); + printf("cPrivatePages = %d\n", cPrivatePages); + printf("cSharedPages = %d\n", cSharedPages); +} +#else +void VBoxServicePageSharingInspectGuest() +{ + /** @todo other platforms */ +} +#endif + + +/** @copydoc VBOXSERVICE::pfnInit */ +static DECLCALLBACK(int) VBoxServicePageSharingInit(void) +{ + printf("VBoxServicePageSharingInit\n"); + +#ifdef RT_OS_WINDOWS + hNtdll = LoadLibrary("ntdll.dll"); + + if (hNtdll) + ZwQuerySystemInformation = (PFNZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwQuerySystemInformation"); +#endif + + /** @todo report system name and version */ + /* Never fail here. */ + return VINF_SUCCESS; +} + +static DECLCALLBACK(void) VBoxServicePageSharingTerm(void) +{ + printf("VBoxServicePageSharingTerm\n"); + +#ifdef RT_OS_WINDOWS + if (hNtdll) + FreeLibrary(hNtdll); +#endif + return; +} + +int main(int argc, char **argv) +{ + /* + * Init globals and such. + */ + int rc = RTR3InitExe(argc, &argv, 0); + if (RT_FAILURE(rc)) + return RTMsgInitFailure(rc); + + /* + * Connect to the kernel part before daemonizing so we can fail + * and complain if there is some kind of problem. We need to initialize + * the guest lib *before* we do the pre-init just in case one of services + * needs do to some initial stuff with it. + */ + printf("Calling VbgR3Init()\n"); + rc = VbglR3Init(); + if (RT_FAILURE(rc)) + { + printf("VbglR3Init failed with rc=%Rrc.\n", rc); + return -1; + } + VBoxServicePageSharingInit(); + + VBoxServicePageSharingInspectGuest(); + + VBoxServicePageSharingTerm(); + return 0; +} + |