Index: ps/trunk/binaries/system/akend.sys =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/akend.sys =================================================================== --- ps/trunk/binaries/system/akend.sys (revision 24136) +++ ps/trunk/binaries/system/akend.sys (nonexistent) Property changes on: ps/trunk/binaries/system/akend.sys ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/system/aken64.pdb =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/aken64.pdb =================================================================== --- ps/trunk/binaries/system/aken64.pdb (revision 24136) +++ ps/trunk/binaries/system/aken64.pdb (nonexistent) Property changes on: ps/trunk/binaries/system/aken64.pdb ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/system/aken64d.pdb =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/aken64d.pdb =================================================================== --- ps/trunk/binaries/system/aken64d.pdb (revision 24136) +++ ps/trunk/binaries/system/aken64d.pdb (nonexistent) Property changes on: ps/trunk/binaries/system/aken64d.pdb ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/system/aken64d.sys =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/aken64d.sys =================================================================== --- ps/trunk/binaries/system/aken64d.sys (revision 24136) +++ ps/trunk/binaries/system/aken64d.sys (nonexistent) Property changes on: ps/trunk/binaries/system/aken64d.sys ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/system/aken64.sys =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/aken64.sys =================================================================== --- ps/trunk/binaries/system/aken64.sys (revision 24136) +++ ps/trunk/binaries/system/aken64.sys (nonexistent) Property changes on: ps/trunk/binaries/system/aken64.sys ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/system/akend.pdb =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/akend.pdb =================================================================== --- ps/trunk/binaries/system/akend.pdb (revision 24136) +++ ps/trunk/binaries/system/akend.pdb (nonexistent) Property changes on: ps/trunk/binaries/system/akend.pdb ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/system/aken.pdb =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/aken.pdb =================================================================== --- ps/trunk/binaries/system/aken.pdb (revision 24136) +++ ps/trunk/binaries/system/aken.pdb (nonexistent) Property changes on: ps/trunk/binaries/system/aken.pdb ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/binaries/system/aken.sys =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: ps/trunk/binaries/system/aken.sys =================================================================== --- ps/trunk/binaries/system/aken.sys (revision 24136) +++ ps/trunk/binaries/system/aken.sys (nonexistent) Property changes on: ps/trunk/binaries/system/aken.sys ___________________________________________________________________ Deleted: svn:mime-type ## -1 +0,0 ## -application/octet-stream \ No newline at end of property Index: ps/trunk/source/lib/sysdep/arch/x86_x64/msr.cpp =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/msr.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/arch/x86_x64/msr.cpp (nonexistent) @@ -1,141 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "precompiled.h" -#include "lib/sysdep/arch/x86_x64/msr.h" - -#include "lib/sysdep/os/win/mahaf.h" -#include "lib/sysdep/arch/x86_x64/x86_x64.h" - - -namespace MSR { - -bool IsAccessible() -{ - if(!x86_x64::Cap(x86_x64::CAP_MSR)) - return false; - - // only read/writable from ring 0, so we need the driver. - if(mahaf_Init() < 0) - return false; - - return true; -} - - -bool HasEnergyPerfBias() -{ -#if 1 - // the documentation is unclear. until it improves, disable - // this, lest we provoke a GPF. - return false; -#else - if(x86_x64::Vendor() != x86_x64::VENDOR_INTEL) - return false; - - if(x86_x64::Family() < 6) - return false; - - if(x86_x64::Model() < 0xE) - return false; - - return true; -#endif -} - - -bool HasPlatformInfo() -{ - if(x86_x64::Vendor() != x86_x64::VENDOR_INTEL) - return false; - - if(x86_x64::Family() != 6) - return false; - - switch(x86_x64::Model()) - { - // section 34.4 in 253665-041US - case x86_x64::MODEL_NEHALEM_EP: - case x86_x64::MODEL_NEHALEM_EP_2: - case x86_x64::MODEL_NEHALEM_EX: - case x86_x64::MODEL_I7_I5: - return true; - - // section 34.5 - case x86_x64::MODEL_CLARKDALE: - case x86_x64::MODEL_WESTMERE_EP: - return true; - - // section 34.6 - case x86_x64::MODEL_WESTMERE_EX: - return true; - - // section 34.7 - case x86_x64::MODEL_SANDY_BRIDGE: - case x86_x64::MODEL_SANDY_BRIDGE_2: - return true; - - default: - return false; - } -} - - -bool HasUncore() -{ - if(x86_x64::Vendor() != x86_x64::VENDOR_INTEL) - return false; - - if(x86_x64::Family() != 6) - return false; - - switch(x86_x64::Model()) - { - // Xeon 5500 / i7 (section B.4.1 in 253669-037US) - case 0x1A: // Bloomfield, Gainstown - case 0x1E: // Clarksfield, Lynnfield, Jasper Forest - case 0x1F: - return true; - - // Xeon 5600 / Westmere (section B.5) - case 0x25: // Clarkdale, Arrandale - case 0x2C: // Gulftown - return true; - - default: - return false; - } -} - - -u64 Read(u64 reg) -{ - return mahaf_ReadModelSpecificRegister(reg); -} - - -void Write(u64 reg, u64 value) -{ - mahaf_WriteModelSpecificRegister(reg, value); -} - -} // namespace MSR Property changes on: ps/trunk/source/lib/sysdep/arch/x86_x64/msr.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/arch/x86_x64/msr.h =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/msr.h (revision 24136) +++ ps/trunk/source/lib/sysdep/arch/x86_x64/msr.h (nonexistent) @@ -1,69 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * model-specific registers - */ - -#ifndef INCLUDED_X86_X64_MSR -#define INCLUDED_X86_X64_MSR - -namespace MSR { - -enum ModelSpecificRegisters -{ - // architectural (will not change on future processors) - IA32_MISC_ENABLE = 0x1A0, - IA32_ENERGY_PERF_BIAS = 0x1B0, // requires HasEnergyPerfBias - - // PMU v1 - IA32_PMC0 = 0x0C1, - IA32_PERFEVTSEL0 = 0x186, - - // PMU v2 - IA32_PERF_GLOBAL_STATUS = 0x38E, - IA32_PERF_GLOBAL_CTRL = 0x38F, - IA32_PERF_GLOBAL_OVF_CTRL = 0x390, - - // Nehalem and later - PLATFORM_INFO = 0x0CE, // requires HasPlatformInfo - - // Nehalem, Westmere (requires HasUncore) - UNCORE_PERF_GLOBAL_CTRL = 0x391, - UNCORE_PERF_GLOBAL_STATUS = 0x392, - UNCORE_PERF_GLOBAL_OVF_CTRL = 0x393, - UNCORE_PMC0 = 0x3B0, - UNCORE_PERFEVTSEL0 = 0x3C0 -}; - -LIB_API bool IsAccessible(); - -LIB_API bool HasEnergyPerfBias(); -LIB_API bool HasPlatformInfo(); -LIB_API bool HasUncore(); - -LIB_API u64 Read(u64 reg); -LIB_API void Write(u64 reg, u64 value); - -} // namespace MSR - -#endif // #ifndef INCLUDED_X86_X64_MSR Property changes on: ps/trunk/source/lib/sysdep/arch/x86_x64/msr.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken/aken_install.bat =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/aken_install.bat (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken/aken_install.bat (nonexistent) @@ -1,87 +0,0 @@ -@ECHO OFF - -"%systemroot%\system32\cacls.exe" "%systemroot%\system32\config\system" >nul 2>&1 -IF ERRORLEVEL 1 GOTO relaunch - -REM detect whether OS is 32/64 bit -IF "%ProgramW6432%" == "%ProgramFiles%" ( - SET aken_bits=64 -) ELSE ( - SET aken_bits=32 -) - -IF "%1" == "enabletest" GOTO enabletest -IF "%1" == "disabletest" GOTO disabletest -IF "%1" == "install" GOTO install -IF "%1" == "remove" GOTO remove -GOTO usage - -:enabletest -bcdedit.exe /set TESTSIGNING ON -GOTO end - -:disabletest -bcdedit.exe /set TESTSIGNING OFF -GOTO end - -:install -IF (%2) == () ( - SET aken_path="%~p0\aken%aken_bits%.sys" -) ELSE ( - echo %2\aken%aken_bits%.sys - SET aken_path=%2\aken%aken_bits%.sys -) -echo %aken_path% -IF NOT EXIST %aken_path% GOTO notfound -sc create Aken DisplayName= Aken type= kernel start= auto binpath= %aken_path% -REM error= normal is default -IF ERRORLEVEL 1 GOTO failed -sc start Aken -IF ERRORLEVEL 1 GOTO failed -ECHO Success! -GOTO end - -:remove -sc stop Aken -sc delete Aken -IF ERRORLEVEL 1 GOTO failed -ECHO Success! (The previous line should read: [SC] DeleteService SUCCESS) -GOTO end - -:usage -ECHO To install the driver, please first enable test mode: -ECHO %0 enabletest -ECHO (This is necessary because Vista/Win7 x64 require signing with -ECHO a Microsoft "cross certificate". The Fraunhofer code signing certificate -ECHO is not enough, even though its chain of trust is impeccable. -ECHO Going the WHQL route, perhaps as an "unclassified" driver, might work. -ECHO see http://www.freeotfe.org/docs/Main/impact_of_kernel_driver_signing.htm ) -ECHO Then reboot (!) and install the driver: -ECHO %0 install ["path_to_directory_containing_aken*.sys"] -ECHO (If no path is given, we will use the directory of this batch file) -ECHO To remove the driver and disable test mode, execute the following: -ECHO %0 remove -ECHO %0 disabletest -PAUSE -GOTO end - -:relaunch -SET aken_vbs="%temp%\aken_run.vbs" -ECHO Set UAC = CreateObject^("Shell.Application"^) > %aken_vbs% -ECHO UAC.ShellExecute "cmd.exe", "/k %~s0 %1 %2", "", "runas", 1 >> %aken_vbs% -ECHO "To re-run this batch file as admin, we have created %aken_vbs% with the following contents:" -type %aken_vbs% -PAUSE -cscript //Nologo %aken_vbs% -DEL %aken_vbs% -GOTO end - -:notfound -ECHO Driver not found at specified path (%aken_path%) -GOTO end - -:failed -ECHO Something went wrong -- see previous line -GOTO end - -:end \ No newline at end of file Property changes on: ps/trunk/source/lib/sysdep/os/win/aken/aken_install.bat ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken/build_all.bat =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/build_all.bat (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken/build_all.bat (nonexistent) @@ -1,32 +0,0 @@ -@echo off -REM ensure header is up to date (simpler than setting include path) -copy ..\..\..\..\..\include\lib\sysdep\os\win\aken\aken.h - -cmd /c build_single.bat chk x64 -cmd /c build_single.bat fre x64 -cmd /c build_single.bat chk x86 -REM must come last because each build_single.bat deletes aken.sys, -REM and that is the final output name of this step -cmd /c build_single.bat fre x86 - -cd amd64 -copy /y aken64*.pdb ..\..\..\..\..\..\..\bin\x64 -copy /y aken64*.sys ..\..\..\..\..\..\..\bin\x64 -cd .. - -cd i386 -copy /y aken*.pdb ..\..\..\..\..\..\..\bin\Win32 -copy /y aken*.sys ..\..\..\..\..\..\..\bin\Win32 -cd .. - -echo outputs copied to bin directory; will delete ALL output files after pressing a key -pause -if exist amd64 (rmdir /S /Q amd64) -if exist i386 (rmdir /S /Q i386) -if exist objchk_wnet_amd64 (rmdir /S /Q objchk_wnet_amd64) -if exist objfre_wnet_amd64 (rmdir /S /Q objfre_wnet_amd64) -if exist objchk_wnet_x86 (rmdir /S /Q objchk_wnet_x86) -if exist objfre_wnet_x86 (rmdir /S /Q objfre_wnet_x86) -if exist *.log (del /Q *.log) -if exist *.err (del /Q *.err) -if exist *.wrn (del /Q *.wrn) \ No newline at end of file Property changes on: ps/trunk/source/lib/sysdep/os/win/aken/build_all.bat ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken/makefile =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/makefile (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken/makefile (nonexistent) @@ -1,7 +0,0 @@ -# -# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source -# file to this component. This file merely indirects to the real make file -# that is shared by all the driver components of the Windows NT DDK -# - -!INCLUDE $(NTMAKEENV)\makefile.def Property changes on: ps/trunk/source/lib/sysdep/os/win/aken/makefile ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken/aken.c =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/aken.c (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken/aken.c (nonexistent) @@ -1,526 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -// note: staticdv cannot yet check C++ code. - -#include -#include "aken.h" -#include "intrinsics.h" - -#define WIN32_NAME L"\\DosDevices\\Aken" -#define DEVICE_NAME L"\\Device\\Aken" - -// placate PREfast -DRIVER_INITIALIZE DriverEntry; -__drv_dispatchType(IRP_MJ_CREATE) DRIVER_DISPATCH AkenCreate; -__drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH AkenClose; -__drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH AkenDeviceControl; -DRIVER_UNLOAD AkenUnload; - -// this driver isn't large, but it's still slightly nicer to make its -// functions pageable and thus not waste precious non-paged pool. -// #pragma code_seg is more convenient than specifying alloc_text for -// every other function. -#pragma alloc_text(INIT, DriverEntry) // => discardable -#pragma code_seg(push, "PAGE") - - -//----------------------------------------------------------------------------- -// memory mapping -//----------------------------------------------------------------------------- - -/* -there are three approaches to mapping physical memory: -(http://www.microsoft.com/whdc/driver/kernel/mem-mgmt.mspx) - -- MmMapIoSpace (http://support.microsoft.com/kb/189327/en-us). despite the - name, it maps physical pages of any kind by allocating PTEs. very easy to - implement, but occupies precious kernel address space. possible bugs: - http://www.osronline.com/showThread.cfm?link=96737 - http://support.microsoft.com/kb/925793/en-us - -- ZwMapViewOfSection of PhysicalMemory (http://tinyurl.com/yozmgy). - the code is a bit bulky, but the WinXP API prevents mapping pages with - conflicting attributes (see below). - -- MmMapLockedPagesSpecifyCache or MmGetSystemAddressForMdlSafe - (http://www.osronline.com/article.cfm?id=423). note: the latter is a macro - that calls the former. this is the 'normal' and fully documented way, - but it doesn't appear able to map a fixed physical address. - (MmAllocatePagesForMdl understandably doesn't work since some pages we - want to map are marked as unavailable for allocation, and I don't see - another documented way to fill an MDL with PFNs.) - -our choice here is forced by a very insidious issue. if someone else has -already mapped a page with different attributes (e.g. cacheable), TLBs -may end up corrupted, leading to disaster. the search for a documented -means of accessing the page frame database (to check if mapped anywhere -and determine the previously set attributes) has not borne fruit, so we -must use ZwMapViewOfSection. (if taking this up again, see -http://www.woodmann.com/forum/archive/index.php/t-6516.html ) - -note that we guess if the page will have been mapped as cacheable and -even try the opposite if that turns out to have been incorrect. -*/ - -static int IsMemoryUncacheable(DWORD64 physicalAddress64) -{ - PAGED_CODE(); - - // original PC memory - contains BIOS - if(physicalAddress64 < 0x100000) - return 1; - - return 0; -} - -static NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWORD64 numBytes64, DWORD64* virtualAddress64) -{ - NTSTATUS ntStatus; - HANDLE hMemory; - LARGE_INTEGER physicalAddress; // convenience - physicalAddress.QuadPart = physicalAddress64; - - PAGED_CODE(); - - // get handle to PhysicalMemory object - { - OBJECT_ATTRIBUTES objectAttributes; - UNICODE_STRING objectName = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory"); - const ULONG attributes = OBJ_CASE_INSENSITIVE; - const HANDLE rootDirectory = 0; - InitializeObjectAttributes(&objectAttributes, &objectName, attributes, rootDirectory, (PSECURITY_DESCRIPTOR)0); - ntStatus = ZwOpenSection(&hMemory, SECTION_ALL_ACCESS, &objectAttributes); - if(!NT_SUCCESS(ntStatus)) - { - KdPrint(("AkenMapPhysicalMemory: ZwOpenSection failed\n")); - return ntStatus; - } - } - - // add a reference (required to prevent the handle from being deleted) - { - PVOID physicalMemorySection = NULL; - const POBJECT_TYPE objectType = 0; // allowed since specifying KernelMode - ntStatus = ObReferenceObjectByHandle(hMemory, SECTION_ALL_ACCESS, objectType, KernelMode, &physicalMemorySection, 0); - if(!NT_SUCCESS(ntStatus)) - { - KdPrint(("AkenMapPhysicalMemory: ObReferenceObjectByHandle failed\n")); - goto close_handle; - } - } - - // note: mapmem.c does HalTranslateBusAddress, but we only care about - // system memory. translating doesn't appear to be necessary, even if - // much existing code uses it (probably due to cargo cult). - - // map desired memory into user PTEs - { - const HANDLE hProcess = (HANDLE)-1; - PVOID virtualBaseAddress = 0; // let ZwMapViewOfSection pick - const ULONG zeroBits = 0; // # high-order bits in address that must be 0 - SIZE_T mappedSize = (SIZE_T)numBytes64; // will receive the actual page-aligned size - LARGE_INTEGER physicalBaseAddress = physicalAddress; // will be rounded down to 64KB boundary - const SECTION_INHERIT inheritDisposition = ViewShare; - const ULONG allocationType = 0; - ULONG protect = PAGE_READWRITE; - if(IsMemoryUncacheable(physicalAddress64)) - protect |= PAGE_NOCACHE; - ntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect); - if(!NT_SUCCESS(ntStatus)) - { - // try again with the opposite cacheability attribute - protect ^= PAGE_NOCACHE; - ntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect); - if(!NT_SUCCESS(ntStatus)) - { - KdPrint(("AkenMapPhysicalMemory: ZwMapViewOfSection failed\n")); - goto close_handle; - } - } - - // the mapping rounded our physical base address down to the nearest - // 64KiB boundary, so adjust the virtual address accordingly. - { - const DWORD32 numBytesRoundedDown = physicalAddress.LowPart - physicalBaseAddress.LowPart; - ASSERT(numBytesRoundedDown < 0x10000); - *virtualAddress64 = (DWORD64)virtualBaseAddress + numBytesRoundedDown; - } - } - - ntStatus = STATUS_SUCCESS; - -close_handle: - // closing the handle even on success means that callers won't have to - // pass it back when unmapping. why does this work? ZwMapViewOfSection - // apparently adds a reference to hMemory. - ZwClose(hMemory); - - return ntStatus; -} - - -static NTSTATUS AkenUnmapPhysicalMemory(const DWORD64 virtualAddress) -{ - PAGED_CODE(); - - { - const HANDLE hProcess = (HANDLE)-1; - PVOID baseAddress = (PVOID)virtualAddress; - NTSTATUS ntStatus = ZwUnmapViewOfSection(hProcess, baseAddress); - if(!NT_SUCCESS(ntStatus)) - { - KdPrint(("AkenUnmapPhysicalMemory: ZwUnmapViewOfSection failed\n")); - return ntStatus; - } - } - - return STATUS_SUCCESS; -} - - -//----------------------------------------------------------------------------- -// helper functions called from DeviceControl -//----------------------------------------------------------------------------- - -static NTSTATUS AkenIoctlReadPort(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - DWORD32 value; - - PAGED_CODE(); - - if(inSize != sizeof(AkenReadPortIn) || *outSize != sizeof(AkenReadPortOut)) - return STATUS_BUFFER_TOO_SMALL; - - { - const AkenReadPortIn* in = (const AkenReadPortIn*)buf; - const USHORT port = in->port; - const UCHAR numBytes = in->numBytes; - switch(numBytes) - { - case 1: - value = (DWORD32)READ_PORT_UCHAR((PUCHAR)port); - break; - case 2: - value = (DWORD32)READ_PORT_USHORT((PUSHORT)port); - break; - case 4: - value = (DWORD32)READ_PORT_ULONG((PULONG)port); - break; - default: - return STATUS_INVALID_PARAMETER; - } - } - - { - AkenReadPortOut* out = (AkenReadPortOut*)buf; - out->value = value; - } - return STATUS_SUCCESS; -} - -static NTSTATUS AkenIoctlWritePort(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - PAGED_CODE(); - - if(inSize != sizeof(AkenWritePortIn) || *outSize != 0) - return STATUS_BUFFER_TOO_SMALL; - - { - const AkenWritePortIn* in = (const AkenWritePortIn*)buf; - const DWORD32 value = in->value; - const USHORT port = in->port; - const UCHAR numBytes = in->numBytes; - switch(numBytes) - { - case 1: - WRITE_PORT_UCHAR((PUCHAR)port, (UCHAR)(value & 0xFF)); - break; - case 2: - WRITE_PORT_USHORT((PUSHORT)port, (USHORT)(value & 0xFFFF)); - break; - case 4: - WRITE_PORT_ULONG((PULONG)port, value); - break; - default: - return STATUS_INVALID_PARAMETER; - } - } - - return STATUS_SUCCESS; -} - - -static NTSTATUS AkenIoctlMap(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - DWORD64 virtualAddress; - NTSTATUS ntStatus; - - PAGED_CODE(); - - if(inSize != sizeof(AkenMapIn) || *outSize != sizeof(AkenMapOut)) - return STATUS_BUFFER_TOO_SMALL; - - { - const AkenMapIn* in = (const AkenMapIn*)buf; - const DWORD64 physicalAddress = in->physicalAddress; - const DWORD64 numBytes = in->numBytes; - ntStatus = AkenMapPhysicalMemory(physicalAddress, numBytes, &virtualAddress); - } - - { - AkenMapOut* out = (AkenMapOut*)buf; - out->virtualAddress = virtualAddress; - } - return ntStatus; -} - -static NTSTATUS AkenIoctlUnmap(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - NTSTATUS ntStatus; - - PAGED_CODE(); - - if(inSize != sizeof(AkenUnmapIn) || *outSize != 0) - return STATUS_BUFFER_TOO_SMALL; - - { - const AkenUnmapIn* in = (const AkenUnmapIn*)buf; - const DWORD64 virtualAddress = in->virtualAddress; - ntStatus = AkenUnmapPhysicalMemory(virtualAddress); - } - - return ntStatus; -} - - -static NTSTATUS AkenIoctlReadModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - DWORD64 value; - - PAGED_CODE(); - - if(inSize != sizeof(AkenReadRegisterIn) || *outSize != sizeof(AkenReadRegisterOut)) - return STATUS_BUFFER_TOO_SMALL; - - { - const AkenReadRegisterIn* in = (const AkenReadRegisterIn*)buf; - const DWORD64 reg = in->reg; - value = __readmsr((int)reg); - } - - { - AkenReadRegisterOut* out = (AkenReadRegisterOut*)buf; - out->value = value; - } - - return STATUS_SUCCESS; -} - -static NTSTATUS AkenIoctlWriteModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - PAGED_CODE(); - - if(inSize != sizeof(AkenWriteRegisterIn) || *outSize != 0) - return STATUS_BUFFER_TOO_SMALL; - - { - const AkenWriteRegisterIn* in = (const AkenWriteRegisterIn*)buf; - const DWORD64 reg = in->reg; - const DWORD64 value = in->value; - __writemsr((unsigned long)reg, value); - } - - return STATUS_SUCCESS; -} - -static NTSTATUS AkenIoctlReadPerformanceMonitoringCounter(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - DWORD64 value; - - PAGED_CODE(); - - if(inSize != sizeof(AkenReadRegisterIn) || *outSize != sizeof(AkenReadRegisterOut)) - return STATUS_BUFFER_TOO_SMALL; - - { - const AkenReadRegisterIn* in = (const AkenReadRegisterIn*)buf; - const DWORD64 reg = in->reg; - value = __readpmc((unsigned long)reg); - } - - { - AkenReadRegisterOut* out = (AkenReadRegisterOut*)buf; - out->value = value; - } - - return STATUS_SUCCESS; -} - - -static NTSTATUS AkenIoctlUnknown(PVOID buf, const ULONG inSize, ULONG* outSize) -{ - PAGED_CODE(); - - KdPrint(("AkenIoctlUnknown\n")); - - *outSize = 0; - return STATUS_INVALID_DEVICE_REQUEST; -} - - -typedef NTSTATUS (*AkenIoctl)(PVOID buf, ULONG inSize, ULONG* outSize); - -static AkenIoctl AkenIoctlFromCode(ULONG ioctlCode) -{ - PAGED_CODE(); - - switch(ioctlCode) - { - case IOCTL_AKEN_READ_PORT: - return AkenIoctlReadPort; - case IOCTL_AKEN_WRITE_PORT: - return AkenIoctlWritePort; - - case IOCTL_AKEN_MAP: - return AkenIoctlMap; - case IOCTL_AKEN_UNMAP: - return AkenIoctlUnmap; - - case IOCTL_AKEN_READ_MSR: - return AkenIoctlReadModelSpecificRegister; - case IOCTL_AKEN_WRITE_MSR: - return AkenIoctlWriteModelSpecificRegister; - - default: - return AkenIoctlUnknown; - } -} - - -//----------------------------------------------------------------------------- -// entry points -//----------------------------------------------------------------------------- - -static NTSTATUS AkenCreate(IN PDEVICE_OBJECT deviceObject, IN PIRP irp) -{ - PAGED_CODE(); - - irp->IoStatus.Status = STATUS_SUCCESS; - irp->IoStatus.Information = 0; - IoCompleteRequest(irp, IO_NO_INCREMENT); - return STATUS_SUCCESS; -} - - -static NTSTATUS AkenClose(IN PDEVICE_OBJECT deviceObject, IN PIRP irp) -{ - PAGED_CODE(); - - // same as AkenCreate ATM - irp->IoStatus.Status = STATUS_SUCCESS; - irp->IoStatus.Information = 0; - IoCompleteRequest(irp, IO_NO_INCREMENT); - return STATUS_SUCCESS; -} - - -static NTSTATUS AkenDeviceControl(IN PDEVICE_OBJECT deviceObject, IN PIRP irp) -{ - PAGED_CODE(); - - { - // get buffer from IRP. all our IOCTLs are METHOD_BUFFERED, so buf is - // allocated by the I/O manager and used for both input and output. - PVOID buf = irp->AssociatedIrp.SystemBuffer; - PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp); - ULONG ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; - const ULONG inSize = irpStack->Parameters.DeviceIoControl.InputBufferLength; - ULONG outSize = irpStack->Parameters.DeviceIoControl.OutputBufferLength; // modified by AkenIoctl* - - const AkenIoctl akenIoctl = AkenIoctlFromCode(ioctlCode); - const NTSTATUS ntStatus = akenIoctl(buf, inSize, &outSize); - - irp->IoStatus.Information = outSize; // number of bytes to copy from buf to user's buffer - irp->IoStatus.Status = ntStatus; - IoCompleteRequest(irp, IO_NO_INCREMENT); - return ntStatus; - } -} - - -static VOID AkenUnload(IN PDRIVER_OBJECT driverObject) -{ - PAGED_CODE(); - - KdPrint(("AkenUnload\n")); - - { - UNICODE_STRING win32Name = RTL_CONSTANT_STRING(WIN32_NAME); - IoDeleteSymbolicLink(&win32Name); - } - - if(driverObject->DeviceObject) - IoDeleteDevice(driverObject->DeviceObject); -} - - -#pragma code_seg(pop) // make sure we don't countermand the alloc_text - -NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath) -{ - UNICODE_STRING deviceName = RTL_CONSTANT_STRING(DEVICE_NAME); - - // create device object - PDEVICE_OBJECT deviceObject; - { - const ULONG deviceExtensionSize = 0; - const ULONG deviceCharacteristics = FILE_DEVICE_SECURE_OPEN; - const BOOLEAN exlusive = TRUE; - NTSTATUS ntStatus = IoCreateDevice(driverObject, deviceExtensionSize, &deviceName, FILE_DEVICE_AKEN, deviceCharacteristics, exlusive, &deviceObject); - if(!NT_SUCCESS(ntStatus)) - { - KdPrint(("DriverEntry: IoCreateDevice failed\n")); - return ntStatus; - } - } - - // set entry points - driverObject->MajorFunction[IRP_MJ_CREATE] = AkenCreate; - driverObject->MajorFunction[IRP_MJ_CLOSE] = AkenClose; - driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AkenDeviceControl; - driverObject->DriverUnload = AkenUnload; - - // symlink NT device name to Win32 namespace - { - UNICODE_STRING win32Name = RTL_CONSTANT_STRING(WIN32_NAME); - NTSTATUS ntStatus = IoCreateSymbolicLink(&win32Name, &deviceName); - if(!NT_SUCCESS(ntStatus)) - { - KdPrint(("DriverEntry: IoCreateSymbolicLink failed\n")); - IoDeleteDevice(deviceObject); - return ntStatus; - } - } - - return STATUS_SUCCESS; -} Property changes on: ps/trunk/source/lib/sysdep/os/win/aken/aken.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken/sources =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/sources (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken/sources (nonexistent) @@ -1,5 +0,0 @@ -TARGETNAME=aken -TARGETPATH=. -TARGETTYPE=DRIVER - -SOURCES=aken.c Property changes on: ps/trunk/source/lib/sysdep/os/win/aken/sources ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken/aken.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/aken.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken/aken.h (nonexistent) @@ -1,116 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Aken driver interface - */ - -// Aken - custodian of the ferryboat to the underworld in Egyptian mythology, -// and a driver that shuttles between applications and kernel mode resources. - -#ifndef INCLUDED_AKEN -#define INCLUDED_AKEN - -#define AKEN_NAME L"Aken" - -// device type -#define FILE_DEVICE_AKEN 53498 // in the "User Defined" range." - -#define AKEN_IOCTL 0x800 // 0x800..0xFFF are for 'customer' use. - -#define IOCTL_AKEN_READ_PORT CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+0, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_AKEN_WRITE_PORT CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+1, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_AKEN_MAP CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+2, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_AKEN_UNMAP CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+3, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_AKEN_READ_MSR CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+4, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_AKEN_WRITE_MSR CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+5, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_AKEN_READ_PMC CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+6, METHOD_BUFFERED, FILE_ANY_ACCESS) - - -// input and output data structures for the IOCTLs - -#pragma pack(push, 1) - -typedef struct AkenReadPortIn_ -{ - USHORT port; - UCHAR numBytes; -} -AkenReadPortIn; - -typedef struct AkenReadPortOut_ -{ - DWORD32 value; -} -AkenReadPortOut; - -typedef struct AkenWritePortIn_ -{ - DWORD32 value; - USHORT port; - UCHAR numBytes; -} -AkenWritePortIn; - -typedef struct AkenMapIn_ -{ - // note: fixed-width types allow the 32 or 64-bit Mahaf wrapper to - // interoperate with the 32 or 64-bit Aken driver. - DWORD64 physicalAddress; - DWORD64 numBytes; -} -AkenMapIn; - -typedef struct AkenMapOut_ -{ - DWORD64 virtualAddress; -} -AkenMapOut; - -typedef struct AkenUnmapIn_ -{ - DWORD64 virtualAddress; -} -AkenUnmapIn; - -typedef struct AkenReadRegisterIn_ -{ - DWORD64 reg; -} -AkenReadRegisterIn; - -typedef struct AkenReadRegisterOut_ -{ - DWORD64 value; -} -AkenReadRegisterOut; - -typedef struct AkenWriteRegisterIn_ -{ - DWORD64 reg; - DWORD64 value; -} -AkenWriteRegisterIn; - -#pragma pack(pop) - -#endif // #ifndef INCLUDED_AKEN Property changes on: ps/trunk/source/lib/sysdep/os/win/aken/aken.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken/build_single.bat =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/build_single.bat (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken/build_single.bat (nonexistent) @@ -1,67 +0,0 @@ -@echo off -REM build a single configuration ({chk,fre} x {x64,x64}) -REM (must be in a separate file to allow invoking in a new cmd - otherwise, -REM setenv complains that the environment have already been set) -REM arguments: chk|fre x64|x86 -if %1==chk (goto configOK) -if %1==fre (goto configOK) -echo first parameter must be either chk or fre -goto :eof -:configOK -if %2==x64 (goto archOK) -if %2==x86 (goto archOK) -echo second parameter must be either x64 or x64 -goto :eof -:archOK - - -call C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ %1 %2 WNET -e: -cd \FOM_Work\Programmierung\lowlevel\src\sysdep\os\win\aken - - -REM delete outputs to ensure they get rebuilt -if %2==x64 (goto delete64) else (goto delete32) - -:delete64 -if not exist amd64 (goto deleteEnd) -cd amd64 -if exist aken.sys (del /Q aken.sys) -if exist aken.pdb (del /Q aken.pdb) -cd .. -goto deleteEnd - -:delete32 -if not exist i386 (goto deleteEnd) -cd i386 -if exist aken.sys (del /Q aken.sys) -if exist aken.pdb (del /Q aken.pdb) -cd .. -goto deleteEnd - -:deleteEnd - - -build - - -REM rename outputs in preparation for build_all's copying them to the binaries directories -if %2==x64 (goto rename64) else (goto rename32) - -:rename64 -if not exist amd64 (goto renameEnd) -cd amd64 -if %1==chk (ren aken.pdb aken64d.pdb) else (ren aken.pdb aken64.pdb) -if %1==chk (ren aken.sys aken64d.sys) else (ren aken.sys aken64.sys) -cd .. -goto renameEnd - -:rename32 -if not exist i386 (goto renameEnd) -cd i386 -if %1==chk (ren aken.pdb akend.pdb) -if %1==chk (ren aken.sys akend.sys) -cd .. -goto renameEnd - -:renameEnd Property changes on: ps/trunk/source/lib/sysdep/os/win/aken/build_single.bat ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/aken =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/aken (nonexistent) Property changes on: ps/trunk/source/lib/sysdep/os/win/aken ___________________________________________________________________ Deleted: svn:ignore ## -1,5 +0,0 ## -*.log -objchk_wxp_x86 -amd64 -i386 -objchk_wnet_AMD64 Index: ps/trunk/source/lib/sysdep/os/win/mahaf.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/mahaf.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/mahaf.cpp (nonexistent) @@ -1,398 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * user-mode interface to Aken driver - */ - -#include "precompiled.h" -#include "lib/sysdep/os/win/mahaf.h" - -#include "lib/config2.h" -#include "lib/module_init.h" - -#include "lib/sysdep/os/win/wutil.h" -#include -#include "lib/sysdep/os/win/aken/aken.h" -#include "lib/sysdep/os/win/wversion.h" - -static HANDLE hAken = INVALID_HANDLE_VALUE; // handle to Aken driver - - -//----------------------------------------------------------------------------- -// ioctl wrappers -//----------------------------------------------------------------------------- - -static u32 ReadPort(u16 port, u8 numBytes) -{ - AkenReadPortIn in; - in.port = (USHORT)port; - in.numBytes = (UCHAR)numBytes; - AkenReadPortOut out; - - DWORD bytesReturned; - LPOVERLAPPED ovl = 0; // synchronous - const BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_READ_PORT, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl); - WARN_RETURN_0_IF_FALSE(ok); - - ENSURE(bytesReturned == sizeof(out)); - return out.value; -} - -u8 mahaf_ReadPort8(u16 port) -{ - const u32 value = ReadPort(port, 1); - ENSURE(value <= 0xFF); - return (u8)(value & 0xFF); -} - -u16 mahaf_ReadPort16(u16 port) -{ - const u32 value = ReadPort(port, 2); - ENSURE(value <= 0xFFFF); - return (u16)(value & 0xFFFF); -} - -u32 mahaf_ReadPort32(u16 port) -{ - const u32 value = ReadPort(port, 4); - return value; -} - - -static void WritePort(u16 port, u32 value, u8 numBytes) -{ - AkenWritePortIn in; - in.value = (DWORD32)value; - in.port = (USHORT)port; - in.numBytes = (UCHAR)numBytes; - - DWORD bytesReturned; // unused but must be passed to DeviceIoControl - LPOVERLAPPED ovl = 0; // synchronous - BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_WRITE_PORT, &in, sizeof(in), 0, 0u, &bytesReturned, ovl); - WARN_IF_FALSE(ok); -} - -void mahaf_WritePort8(u16 port, u8 value) -{ - WritePort(port, (u32)value, 1); -} - -void mahaf_WritePort16(u16 port, u16 value) -{ - WritePort(port, (u32)value, 2); -} - -void mahaf_WritePort32(u16 port, u32 value) -{ - WritePort(port, value, 4); -} - - -bool mahaf_IsPhysicalMappingDangerous() -{ - // pre-XP versions don't prevent re-mapping pages with incompatible - // attributes, which may lead to disaster due to TLB corruption. - if(wversion_Number() < WVERSION_XP) - return true; - - return false; -} - - -volatile void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes) -{ - ENSURE(!mahaf_IsPhysicalMappingDangerous()); - - AkenMapIn in; - in.physicalAddress = (DWORD64)physicalAddress; - in.numBytes = (DWORD64)numBytes; - AkenMapOut out; - - DWORD bytesReturned; - LPOVERLAPPED ovl = 0; // synchronous - const BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_MAP, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl); - WARN_RETURN_0_IF_FALSE(ok); - - ENSURE(bytesReturned == sizeof(out)); - volatile void* virtualAddress = (volatile void*)(uintptr_t)out.virtualAddress; - return virtualAddress; -} - - -void mahaf_UnmapPhysicalMemory(volatile void* virtualAddress) -{ - ENSURE(!mahaf_IsPhysicalMappingDangerous()); - - AkenUnmapIn in; - in.virtualAddress = (DWORD64)virtualAddress; - - DWORD bytesReturned; // unused but must be passed to DeviceIoControl - LPOVERLAPPED ovl = 0; // synchronous - BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_UNMAP, &in, sizeof(in), 0, 0u, &bytesReturned, ovl); - WARN_IF_FALSE(ok); -} - - -static u64 ReadRegister(DWORD ioctl, u64 reg) -{ - AkenReadRegisterIn in; - in.reg = reg; - AkenReadRegisterOut out; - - DWORD bytesReturned; - LPOVERLAPPED ovl = 0; // synchronous - const BOOL ok = DeviceIoControl(hAken, ioctl, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl); - WARN_RETURN_0_IF_FALSE(ok); - - ENSURE(bytesReturned == sizeof(out)); - return out.value; -} - -u64 mahaf_ReadModelSpecificRegister(u64 reg) -{ - return ReadRegister((DWORD)IOCTL_AKEN_READ_MSR, reg); -} - -u64 mahaf_ReadPerformanceMonitoringCounter(u64 reg) -{ - return ReadRegister((DWORD)IOCTL_AKEN_READ_PMC, reg); -} - -void mahaf_WriteModelSpecificRegister(u64 reg, u64 value) -{ - AkenWriteRegisterIn in; - in.reg = reg; - in.value = value; - - DWORD bytesReturned; // unused but must be passed to DeviceIoControl - LPOVERLAPPED ovl = 0; // synchronous - BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_WRITE_MSR, &in, sizeof(in), 0, 0u, &bytesReturned, ovl); - WARN_IF_FALSE(ok); -} - - -//----------------------------------------------------------------------------- -// driver installation -//----------------------------------------------------------------------------- - -// @param access need not include SC_MANAGER_CONNECT ("implicitly specified") -static SC_HANDLE OpenServiceControlManager(DWORD access) -{ - LPCWSTR machineName = 0; // local - LPCWSTR databaseName = 0; // default - SC_HANDLE hSCM = OpenSCManagerW(machineName, databaseName, access); - if(!hSCM) - { - // ensure no other problems arose - ENSURE(GetLastError() == ERROR_ACCESS_DENIED); - - // administrator privileges are required for SC_MANAGER_CREATE_SERVICE. - // this is a problem on Vista / Win7, so users will have to use the - // separate aken_install.bat - - return 0; - } - - return hSCM; // success -} - - -static void UninstallDriver() -{ - SC_HANDLE hSCM = OpenServiceControlManager(SC_MANAGER_ENUMERATE_SERVICE); - if(!hSCM) - return; - SC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_STOP|SERVICE_INTERROGATE); - if(!hService) - return; - - // stop service - SERVICE_STATUS serviceStatus; - if(!ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus)) - { - // if the problem wasn't that the service is already stopped, - // something actually went wrong. - const DWORD err = GetLastError(); - ENSURE(err == ERROR_SERVICE_NOT_ACTIVE || err == ERROR_SERVICE_CANNOT_ACCEPT_CTRL); - } - - // delete service - BOOL ok; - ok = DeleteService(hService); - WARN_IF_FALSE(ok); - ok = CloseServiceHandle(hService); - WARN_IF_FALSE(ok); - - ok = CloseServiceHandle(hSCM); - WARN_IF_FALSE(ok); -} - - -#if CONFIG2_MAHAF_ATTEMPT_DRIVER_START - -static void StartDriver(const OsPath& driverPathname) -{ - const SC_HANDLE hSCM = OpenServiceControlManager(SC_MANAGER_CREATE_SERVICE); - if(!hSCM) - { - ENSURE(GetLastError() == ERROR_ACCESS_DENIED); - SetLastError(0); - return; - } - - SC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_START); - - // during development, we want to ensure the newest build is used, so - // unload and re-create the service if it's running/installed. - // as of 2008-03-24 no further changes to Aken are pending, so this is - // disabled (thus also avoiding trouble when running multiple instances) -#if 0 - if(hService) - { - BOOL ok = CloseServiceHandle(hService); - WARN_IF_FALSE(ok); - hService = 0; - UninstallDriver(); - } -#endif - - // create service (note: this just enters the service into SCM's DB; - // no error is raised if the driver binary doesn't exist etc.) - if(!hService) - { - LPCWSTR startName = 0; // LocalSystem - // NB: Windows 7 seems to insist upon backslashes (i.e. external_file_string) - hService = CreateServiceW(hSCM, AKEN_NAME, AKEN_NAME, - SERVICE_START, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, - OsString(driverPathname).c_str(), 0, 0, 0, startName, 0); - ENSURE(hService != 0); - } - - { - DWORD numArgs = 0; - BOOL ok = StartService(hService, numArgs, 0); - if(!ok) - { - switch(GetLastError()) - { - case ERROR_SERVICE_ALREADY_RUNNING: - // ok, no action needed - break; - case ERROR_ACCESS_DENIED: - // Win7, can't start service; must use aken_install.bat - break; - case ERROR_INVALID_IMAGE_HASH: - // Win7 x86 rejects our code signing certificate; must enable - // "test signing" mode via aken_install.bat - break; - default: - // unexpected problem - DEBUG_WARN_ERR(ERR::LOGIC); - break; - } - } - } - - CloseServiceHandle(hService); - CloseServiceHandle(hSCM); -} - - -static bool Is64BitOs() -{ -#if OS_WIN64 - return true; -#else - return wutil_IsWow64(); -#endif -} - -static OsPath DriverPathname() -{ - const char* const bits = Is64BitOs()? "64" : ""; -#ifdef NDEBUG - const char* const debug = ""; -#else - const char* const debug = "d"; -#endif - char filename[PATH_MAX]; - sprintf_s(filename, ARRAY_SIZE(filename), "aken%s%s.sys", bits, debug); - return wutil_ExecutablePath() / filename; -} - -#endif // CONFIG2_MAHAF_ATTEMPT_DRIVER_START - - -//----------------------------------------------------------------------------- - -static Status Init() -{ - WinScopedPreserveLastError s; - - if(wutil_HasCommandLineArgument(L"-wNoMahaf")) - return ERR::NOT_SUPPORTED; // NOWARN - -#if CONFIG2_MAHAF_ATTEMPT_DRIVER_START - { - const OsPath driverPathname = DriverPathname(); - StartDriver(driverPathname); - } -#endif - - { - const DWORD shareMode = 0; - hAken = CreateFileW(L"\\\\.\\Aken", GENERIC_READ, shareMode, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - if(hAken == INVALID_HANDLE_VALUE) - { - // GetLastError() is ERROR_FILE_NOT_FOUND if the driver isn't running, - // but this is also reached when a handle has already been opened - // (e.g. by a second instance of the same program) - in which case we must - // indicate failure so that clients won't engage in unsynchronized ring 0 operations. - SetLastError(0); - return ERR::INVALID_HANDLE; // NOWARN (happens often due to security restrictions) - } - } - - return INFO::OK; -} - -static void Shutdown() -{ - CloseHandle(hAken); - hAken = INVALID_HANDLE_VALUE; - - UninstallDriver(); -} - - -static ModuleInitState initState; - -Status mahaf_Init() -{ - return ModuleInit(&initState, Init); -} - -void mahaf_Shutdown() -{ - ModuleShutdown(&initState, Shutdown); -} Property changes on: ps/trunk/source/lib/sysdep/os/win/mahaf.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/tgt.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tgt.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/tgt.cpp (nonexistent) @@ -1,106 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using timeGetTime - */ - -// note: WinMM is delay-loaded to avoid dragging it in when this timer -// implementation isn't used. (this is relevant because its startup is -// fairly slow) - -#include "precompiled.h" -#include "lib/sysdep/os/win/whrt/tgt.h" - -#include "lib/sysdep/os/win/whrt/counter.h" - -#include "lib/sysdep/os/win/win.h" -#include - -#if MSC_VERSION -#pragma comment(lib, "winmm.lib") -#endif - - -// "Guidelines For Providing Multimedia Timer Support" claims that -// speeding the timer up to 2 ms has little impact, while 1 ms -// causes significant slowdown. -static const UINT PERIOD_MS = 2; - -class CounterTGT : public ICounter -{ -public: - virtual const char* Name() const - { - return "TGT"; - } - - Status Activate() - { - // note: timeGetTime is always available and cannot fail. - - MMRESULT ret = timeBeginPeriod(PERIOD_MS); - ENSURE(ret == TIMERR_NOERROR); - - return INFO::OK; - } - - void Shutdown() - { - timeEndPeriod(PERIOD_MS); - } - - bool IsSafe() const - { - // the only point of criticism is the possibility of falling behind - // due to lost interrupts. this can happen to any interrupt-based timer - // and some systems may lack a counter-based timer, so consider TGT - // 'safe'. note that it is still only chosen when all other timers fail. - return true; - } - - u64 Counter() const - { - return timeGetTime(); - } - - size_t CounterBits() const - { - return 32; - } - - double NominalFrequency() const - { - return 1000.0; - } - - double Resolution() const - { - return PERIOD_MS*1e-3; - } -}; - -ICounter* CreateCounterTGT(void* address, size_t size) -{ - ENSURE(sizeof(CounterTGT) <= size); - return new(address) CounterTGT(); -} Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/tgt.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/tsc.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tsc.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/tsc.h (nonexistent) @@ -1,33 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using RDTSC - */ - -#ifndef INCLUDED_TSC -#define INCLUDED_TSC - -class ICounter; -extern ICounter* CreateCounterTSC(void* address, size_t size); - -#endif // #ifndef INCLUDED_TSC Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/tsc.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/counter.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/counter.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/counter.cpp (nonexistent) @@ -1,117 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Interface for counter implementations - */ - -#include "precompiled.h" -#include "lib/sysdep/os/win/whrt/counter.h" - -#include "lib/alignment.h" -#include "lib/sysdep/cpu.h" // cpu_CAS - -#include "lib/sysdep/os/win/whrt/tsc.h" -#include "lib/sysdep/os/win/whrt/hpet.h" -#include "lib/sysdep/os/win/whrt/pmt.h" -#include "lib/sysdep/os/win/whrt/qpc.h" -#include "lib/sysdep/os/win/whrt/tgt.h" -// to add a new counter type, simply include its header here and -// insert a case in ConstructCounterAt's switch statement. - - -//----------------------------------------------------------------------------- -// create/destroy counters - -/** - * @param id - * @param address - * @param size Maximum allowable size [bytes] of the subclass instance - * @return pointer to a newly constructed ICounter subclass of type \ at - * the given address, or 0 iff the ID is invalid. - **/ -static ICounter* ConstructCounterAt(size_t id, void* address, size_t size) -{ - // rationale for placement new: see call site. - - // counters are chosen according to the following order. rationale: - // - TSC must come before QPC and PMT to make sure a bug in the latter on - // Pentium systems doesn't come up. - // - PMT works, but is inexplicably slower than QPC on a PIII Mobile. - // - TGT really isn't as safe as the others, so it should be last. - // - low-overhead and high-resolution counters are preferred. - switch(id) - { - case 0: - return CreateCounterHPET(address, size); - case 1: - return CreateCounterTSC(address, size); - case 2: - return CreateCounterQPC(address, size); - case 3: - return CreateCounterPMT(address, size); - case 4: - return CreateCounterTGT(address, size); - default: - return 0; - } -} - - -static volatile intptr_t isCounterAllocated; - -ICounter* CreateCounter(size_t id) -{ - // we placement-new the Counter classes in a static buffer. - // this is dangerous, but we are careful to ensure alignment. it is - // unusual and thus bad, but there's also one advantage: we avoid - // using global operator new before the CRT is initialized (risky). - // - // alternatives: - // - defining as static doesn't work because the ctors (necessary for - // vptr initialization) run during _cinit, which comes after our - // first use of them. - // - using static_calloc isn't possible because we don't know the - // size until after the alloc / placement new. - - if(!cpu_CAS(&isCounterAllocated, 0, 1)) - DEBUG_WARN_ERR(ERR::LOGIC); // static counter memory is already in use! - - static const size_t memSize = 200; - static u8 mem[memSize]; - u8* alignedMem = (u8*)Align<16>((uintptr_t)mem); - const size_t bytesLeft = mem+memSize - alignedMem; - ICounter* counter = ConstructCounterAt(id, alignedMem, bytesLeft); - - return counter; -} - - -void DestroyCounter(ICounter*& counter) -{ - ENSURE(counter); - counter->Shutdown(); - counter->~ICounter(); // must be called due to placement new - counter = 0; - - isCounterAllocated = 0; -} Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/counter.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/hpet.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/hpet.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/hpet.cpp (nonexistent) @@ -1,233 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using High Precision Event Timer - */ - -#include "precompiled.h" -#include "lib/sysdep/os/win/whrt/hpet.h" - -// for atomic 64-bit read/write: -#define HAVE_X64_MOVD ARCH_AMD64 && (ICC_VERSION || MSC_VERSION >= 1500) -#if HAVE_X64_MOVD -# include -#else -# include -#endif - -#include "lib/sysdep/os/win/whrt/counter.h" - -#include "lib/sysdep/os/win/win.h" -#include "lib/sysdep/os/win/mahaf.h" -#include "lib/sysdep/acpi.h" -#include "lib/bits.h" - - -class CounterHPET : public ICounter -{ -public: - CounterHPET() - : m_hpetRegisters(0) - { - } - - virtual const char* Name() const - { - return "HPET"; - } - - Status Activate() - { - RETURN_STATUS_IF_ERR(MapRegisters(m_hpetRegisters)); - - RETURN_STATUS_IF_ERR(VerifyCapabilities(m_frequency, m_counterBits)); - - // start the counter (if not already running) - Write64(CONFIG, Read64(CONFIG)|1); - // note: to avoid interfering with any other users of the timer - // (e.g. Vista QPC), we don't reset the counter value to 0. - - return INFO::OK; - } - - void Shutdown() - { - if(m_hpetRegisters) - { - mahaf_UnmapPhysicalMemory((void*)m_hpetRegisters); - m_hpetRegisters = 0; - } - } - - bool IsSafe() const - { - // the HPET having been created to address other timers' problems, - // it has no issues of its own. - return true; - } - - u64 Counter() const - { - // notes: - // - Read64 is atomic and avoids race conditions. - // - 32-bit counters (m_counterBits == 32) still allow - // reading the whole register (the upper bits are zero). - return Read64(COUNTER_VALUE); - } - - size_t CounterBits() const - { - return m_counterBits; - } - - double NominalFrequency() const - { - return m_frequency; - } - - double Resolution() const - { - return 1.0 / m_frequency; - } - -private: -#pragma pack(push, 1) - - struct HpetDescriptionTable - { - AcpiTable header; - u32 eventTimerBlockId; - AcpiGenericAddress baseAddress; - u8 sequenceNumber; - u16 minimumPeriodicTicks; - u8 attributes; - }; - -#pragma pack(pop) - - enum RegisterOffsets - { - CAPS_AND_ID = 0x00, - CONFIG = 0x10, - COUNTER_VALUE = 0xF0, - MAX_OFFSET = 0x3FF - }; - - static Status MapRegisters(volatile void*& registers) - { - if(mahaf_IsPhysicalMappingDangerous()) - return ERR::FAIL; // NOWARN (happens on Win2k) - RETURN_STATUS_IF_ERR(mahaf_Init()); // (fails without Administrator privileges) - - const HpetDescriptionTable* hpet = (const HpetDescriptionTable*)acpi_GetTable("HPET"); - if(!hpet) - return ERR::NOT_SUPPORTED; // NOWARN (HPET not reported by BIOS) - - if(hpet->baseAddress.addressSpaceId != ACPI_AS_MEMORY) - return ERR::NOT_SUPPORTED; // NOWARN (happens on some BIOSes) - // hpet->baseAddress.accessSize is reserved - const uintptr_t address = uintptr_t(hpet->baseAddress.address); - ENSURE(address % 8 == 0); // "registers are generally aligned on 64-bit boundaries" - - registers = mahaf_MapPhysicalMemory(address, MAX_OFFSET+1); - if(!registers) - WARN_RETURN(ERR::NO_MEM); - - return INFO::OK; - } - - // note: this is atomic even on 32-bit CPUs (Pentium MMX and - // above have a 64-bit data bus and MOVQ instruction) - u64 Read64(size_t offset) const - { - ENSURE(offset <= MAX_OFFSET); - ENSURE(offset % 8 == 0); - const uintptr_t address = uintptr_t(m_hpetRegisters)+offset; - const __m128i value128 = _mm_loadl_epi64((__m128i*)address); -#if HAVE_X64_MOVD - return _mm_cvtsi128_si64x(value128); -#else - __declspec(align(16)) u32 values[4]; - _mm_store_si128((__m128i*)values, value128); - return u64_from_u32(values[1], values[0]); -#endif - } - - void Write64(size_t offset, u64 value) const - { - ENSURE(offset <= MAX_OFFSET); - ENSURE(offset % 8 == 0); - ENSURE(offset != CAPS_AND_ID); // can't write to read-only registers - const uintptr_t address = uintptr_t(m_hpetRegisters)+offset; -#if HAVE_X64_MOVD - const __m128i value128 = _mm_cvtsi64x_si128(value); -#else - const __m128i value128 = _mm_set_epi32(0, 0, int(value >> 32), int(value & 0xFFFFFFFF)); -#endif - _mm_storel_epi64((__m128i*)address, value128); - } - - Status VerifyCapabilities(double& frequency, u32& counterBits) const - { - // AMD document 43366 indicates the clock generator that drives the - // HPET is "spread-capable". Wikipedia's frequency hopping article - // explains that this reduces electromagnetic interference. - // The AMD document recommends BIOS writers add SMM hooks for - // reporting the resulting slightly different frequency. - // This apparently requires calibration triggered when the HPET is - // accessed, during which the config register is -1. We'll wait - // about 1 ms (MMIO is expected to take at least 1 us) and - // then ensure the HPET timer period is within reasonable bounds. - u64 caps_and_id = Read64(CAPS_AND_ID); - for(size_t reps = 0; reps < 1000; reps++) - { - if(caps_and_id != ~u64(0)) // register seems valid - break; - caps_and_id = Read64(CAPS_AND_ID); - } - - const u8 revision = (u8)bits(caps_and_id, 0, 7); - ENSURE(revision != 0); // "the value must NOT be 00h" - counterBits = (caps_and_id & Bit(13))? 64 : 32; - const u16 vendorID = (u16)bits(caps_and_id, 16, 31); - const u32 period_fs = (u32)bits(caps_and_id, 32, 63); - ENSURE(period_fs != 0); // "a value of 0 in this field is not permitted" - frequency = 1e15 / period_fs; - debug_printf("HPET: rev=%X vendor=%X bits=%d period=%08X freq=%g\n", revision, vendorID, counterBits, period_fs, frequency); - - if(period_fs > 0x05F5E100) // 100 ns (spec guarantees >= 10 MHz) - return ERR::CORRUPTED; // avoid using HPET (e.g. if calibration was still in progress) - - return INFO::OK; - } - - volatile void* m_hpetRegisters; - double m_frequency; - u32 m_counterBits; -}; - -ICounter* CreateCounterHPET(void* address, size_t size) -{ - ENSURE(sizeof(CounterHPET) <= size); - return new(address) CounterHPET(); -} Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/hpet.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/pmt.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/pmt.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/pmt.cpp (nonexistent) @@ -1,115 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using ACPI PM timer - */ - -#include "precompiled.h" -#include "lib/sysdep/os/win/whrt/pmt.h" - -#include "lib/sysdep/os/win/whrt/counter.h" - -#include "lib/sysdep/os/win/win.h" -#include "lib/sysdep/acpi.h" -#include "lib/sysdep/os/win/mahaf.h" -#include "lib/bits.h" - -static const u32 TMR_VAL_EXT = Bit(8); // FADT flags - -//----------------------------------------------------------------------------- - - -class CounterPMT : public ICounter -{ -public: - CounterPMT() - : m_portAddress(0xFFFF) - { - } - - virtual const char* Name() const - { - return "PMT"; - } - - Status Activate() - { - // mahaf is needed for port I/O. - RETURN_STATUS_IF_ERR(mahaf_Init()); // (fails without Administrator privileges) - // (note: it's called FADT, but the signature is "FACP") - const FADT* fadt = (const FADT*)acpi_GetTable("FACP"); - if(!fadt) - return ERR::NOT_SUPPORTED; // NOWARN (ACPI tables might not be available) - m_portAddress = u16_from_larger(fadt->pmTimerPortAddress); - - return INFO::OK; - } - - void Shutdown() - { - } - - bool IsSafe() const - { - // the PMT has one issue: "Performance counter value may unexpectedly - // leap forward" (Q274323). This happens on some buggy Pentium-era - // systems under heavy PCI bus load. We are clever and observe that - // the TSC implementation would be used on such systems (because it - // has higher precedence and is safe on P5 CPUs), so the PMT is fine - // in general. - return true; - } - - u64 Counter() const - { - return mahaf_ReadPort32(m_portAddress); - } - - size_t CounterBits() const - { - // (see previous acpi_GetTable call) - const FADT* fadt = (const FADT*)acpi_GetTable("FACP"); - ENSURE(fadt); // Activate made sure FADT is available - const size_t counterBits = (fadt->flags & TMR_VAL_EXT)? 32 : 24; - return counterBits; - } - - double NominalFrequency() const - { - return (double)PMT_FREQ; - } - - double Resolution() const - { - return 1.0 / PMT_FREQ; - } - -private: - u16 m_portAddress; -}; - -ICounter* CreateCounterPMT(void* address, size_t size) -{ - ENSURE(sizeof(CounterPMT) <= size); - return new(address) CounterPMT(); -} Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/pmt.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/pit.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/pit.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/pit.h (nonexistent) @@ -1,41 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using 82C53/4 PIT - */ - -#ifndef INCLUDED_PIT -#define INCLUDED_PIT - -// note: we don't access the PIT for two reasons: -// - it rolls over every 55 ms (1.193 MHz, 16 bit) and would have to be -// read at least that often, which would require setting high thread -// priority (dangerous). -// - reading it is slow and cannot be done by two independent users -// (the second being QPC) since the counter value must be latched. -// -// there are enough other counters anway. - -static const i64 PIT_FREQ = 1193182; // (= master oscillator frequency/12) - -#endif // #ifndef INCLUDED_PIT Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/pit.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/qpc.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/qpc.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/qpc.cpp (nonexistent) @@ -1,157 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using QueryPerformanceCounter - */ - -#include "precompiled.h" -#include "lib/sysdep/os/win/whrt/qpc.h" - -#include "lib/sysdep/os/win/whrt/counter.h" - -#include "lib/sysdep/os_cpu.h" -#include "lib/sysdep/os/win/win.h" -#include "lib/sysdep/os/win/wutil.h" // wutil_argv -#include "lib/sysdep/os/win/whrt/pit.h" // PIT_FREQ -#include "lib/sysdep/os/win/whrt/pmt.h" // PMT_FREQ - - -class CounterQPC : public ICounter -{ -public: - CounterQPC() - : m_frequency(-1) - { - } - - virtual const char* Name() const - { - return "QPC"; - } - - Status Activate() - { - // note: QPC is observed to be universally supported, but the API - // provides for failure, so play it safe. - - LARGE_INTEGER qpcFreq, qpcValue; - const BOOL ok1 = QueryPerformanceFrequency(&qpcFreq); - const BOOL ok2 = QueryPerformanceCounter(&qpcValue); - if(!ok1 || !ok2) - WARN_RETURN(ERR::FAIL); - if(!qpcFreq.QuadPart || !qpcValue.QuadPart) - WARN_RETURN(ERR::FAIL); - - m_frequency = (i64)qpcFreq.QuadPart; - return INFO::OK; - } - - void Shutdown() - { - } - - bool IsSafe() const - { - // note: we have separate modules that directly access some of the - // counters potentially used by QPC. disabling the redundant counters - // would be ugly (increased coupling). instead, we'll make sure our - // implementations could (if necessary) coexist with QPC, but it - // shouldn't come to that since only one counter is needed/used. - - // the PIT is entirely safe (even if annoyingly slow to read) - if(m_frequency == PIT_FREQ) - return true; - - // the PMT is generally safe (see discussion in CounterPmt::IsSafe), - // but older QPC implementations had problems with 24-bit rollover. - // "System clock problem can inflate benchmark scores" - // (http://www.lionbridge.com/bi/cont2000/200012/perfcnt.asp ; no longer - // online, nor findable in Google Cache / archive.org) tells of - // incorrect values every 4.6 seconds (i.e. 24 bits @ 3.57 MHz) unless - // the timer is polled in the meantime. fortunately, this is guaranteed - // by our periodic updates (which come at least that often). - if(m_frequency == PMT_FREQ) - return true; - - // the TSC has been known to be buggy (even mentioned in MSDN). it is - // used on MP HAL systems and can be detected by comparing QPF with the - // CPU clock. we consider it unsafe unless the user promises (via - // command line) that it's patched and thus reliable on their system. - bool usesTsc = IsSimilarMagnitude((double)m_frequency, os_cpu_ClockFrequency()); - // unconfirmed reports indicate QPC sometimes uses 1/3 of the - // CPU clock frequency, so check that as well. - usesTsc |= IsSimilarMagnitude((double)m_frequency, os_cpu_ClockFrequency()/3); - if(usesTsc) - { - const bool isTscSafe = wutil_HasCommandLineArgument(L"-wQpcTscSafe"); - return isTscSafe; - } - - // the HPET is reliable and used on Vista. it can't easily be recognized - // since its frequency is variable (the spec says > 10 MHz; the master - // 14.318 MHz oscillator is often used). considering frequencies in - // [10, 100 MHz) to be a HPET would be dangerous because it may actually - // be faster or RDTSC slower. we have to exclude all other cases and - // assume it's a HPET - and thus safe - if we get here. - return true; - } - - u64 Counter() const - { - // fairly time-critical here, don't check the return value - // (IsSupported made sure it succeeded initially) - LARGE_INTEGER qpc_value; - (void)QueryPerformanceCounter(&qpc_value); - return qpc_value.QuadPart; - } - - size_t CounterBits() const - { - // there are reports of incorrect rollover handling in the PMT - // implementation of QPC (see CounterPMT::IsSafe). however, other - // counters would be used on those systems, so it's irrelevant. - // we'll report the full 64 bits. - return 64; - } - - double NominalFrequency() const - { - return (double)m_frequency; - } - - double Resolution() const - { - return 1.0 / m_frequency; - } - -private: - // used in several places and QPF is a bit slow+cumbersome. - // (i64 allows easier conversion to double) - i64 m_frequency; -}; - -ICounter* CreateCounterQPC(void* address, size_t size) -{ - ENSURE(sizeof(CounterQPC) <= size); - return new(address) CounterQPC(); -} Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/qpc.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/tgt.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tgt.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/tgt.h (nonexistent) @@ -1,33 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using timeGetTime - */ - -#ifndef INCLUDED_TGT -#define INCLUDED_TGT - -class ICounter; -extern ICounter* CreateCounterTGT(void* address, size_t size); - -#endif // #ifndef INCLUDED_TGT Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/tgt.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/counter.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/counter.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/counter.h (nonexistent) @@ -1,91 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Interface for counter implementations - */ - -#ifndef INCLUDED_COUNTER -#define INCLUDED_COUNTER - -// derived implementations must be called CounterIMPL, -// where IMPL matches the WHRT_IMPL identifier. (see CREATE) -class ICounter -{ -public: - // (compiled-generated) ctor only sets up the vptr - virtual ~ICounter() {} - - virtual const char* Name() const = 0; - - // Activate with an error return value is much cleaner+safer than - // throwing exceptions in the ctor. - virtual Status Activate() = 0; - virtual void Shutdown() = 0; - - virtual bool IsSafe() const = 0; - - /** - * @return the current value of the counter (all but the lower - * CounterBits() bits must be zero) - **/ - virtual u64 Counter() const = 0; - - // note: implementations need not cache the following; that's taken - // care of by WHRT. - - /** - * @return the bit width of the counter (<= 64) - * WHRT uses this to ensure the counter (running at nominal frequency) - * doesn't overflow more than once during CALIBRATION_INTERVAL_MS. - **/ - virtual size_t CounterBits() const = 0; - - /** - * initial measurement of the tick rate. not necessarily correct - * (e.g. when using TSC: os_cpu_ClockFrequency isn't exact). - **/ - virtual double NominalFrequency() const = 0; - - /** - * actual resolution [s]. differs from 1/NominalFrequency if the - * timer adjustment is greater than 1 tick. - **/ - virtual double Resolution() const = 0; -}; - - -/** - * @return a newly created ICounter of type \ or 0 iff the ID is invalid. - * @param id integer ID (0..N-1) - * - * there can only be one active counter at a time; the previous one must - * have been destroyed before creating another! - **/ -extern ICounter* CreateCounter(size_t id); - -/** - * shut down the counter, free its resources and zero its pointer. - **/ -extern void DestroyCounter(ICounter*& counter); - -#endif // #ifndef INCLUDED_COUNTER Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/counter.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/whrt.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/whrt.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/whrt.cpp (nonexistent) @@ -1,313 +0,0 @@ -/* Copyright (C) 2018 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Windows High Resolution Timer - */ - -#include "precompiled.h" -#include "lib/sysdep/os/win/whrt/whrt.h" - -#include // _beginthreadex - -#include "lib/sysdep/cpu.h" -#include "lib/sysdep/os/win/wutil.h" -#include "lib/sysdep/os/win/winit.h" -#include "lib/sysdep/acpi.h" -#include "lib/bits.h" - -#include "lib/sysdep/os/win/whrt/counter.h" - -WINIT_REGISTER_EARLY_INIT2(whrt_Init); // wutil -> whrt -> wtime -WINIT_REGISTER_LATE_SHUTDOWN(whrt_Shutdown); - - -namespace ERR -{ - const Status WHRT_COUNTER_UNSAFE = 140000; -} - - -//----------------------------------------------------------------------------- -// choose best available safe counter - -// (moved into a separate function to simplify error handling) -static inline Status ActivateCounter(ICounter* counter) -{ - RETURN_STATUS_IF_ERR(counter->Activate()); - - if(!counter->IsSafe()) - return ERR::WHRT_COUNTER_UNSAFE; // NOWARN (happens often) - - return INFO::OK; -} - -/** - * @return the newly created and unique instance of the next best counter - * that is deemed safe, or 0 if all have already been created. - **/ -static ICounter* GetNextBestSafeCounter() -{ - for(;;) - { - static size_t nextCounterId = 0; - ICounter* counter = CreateCounter(nextCounterId++); - if(!counter) - return 0; // tried all, none were safe - - Status err = ActivateCounter(counter); - if(err == INFO::OK) - { - debug_printf("HRT: using name=%s freq=%f\n", counter->Name(), counter->NominalFrequency()); - return counter; // found a safe counter - } - else - { - wchar_t buf[100]; - debug_printf("HRT: activating %s failed: %s\n", counter->Name(), utf8_from_wstring(StatusDescription(err, buf, ARRAY_SIZE(buf))).c_str()); - DestroyCounter(counter); - } - } -} - - -//----------------------------------------------------------------------------- -// counter that drives the timer - -static ICounter* counter; -// (these counter properties are cached for efficiency and convenience:) -static double nominalFrequency; -static double resolution; -static size_t counterBits; -static u64 counterMask; - -static void InitCounter() -{ - // we used to support switching counters at runtime, but that's - // unnecessarily complex. it need and should only be done once. - ENSURE(counter == 0); - counter = GetNextBestSafeCounter(); - ENSURE(counter != 0); - - nominalFrequency = counter->NominalFrequency(); - resolution = counter->Resolution(); - counterBits = counter->CounterBits(); - debug_printf("HRT: counter=%s freq=%g res=%g bits=%d\n", counter->Name(), nominalFrequency, resolution, counterBits); - - // sanity checks - ENSURE(nominalFrequency >= 500.0-DBL_EPSILON); - ENSURE(resolution <= 2e-3); - ENSURE(8 <= counterBits && counterBits <= 64); - - counterMask = bit_mask(counterBits); -} - -static void ShutdownCounter() -{ - DestroyCounter(counter); -} - -static inline u64 Counter() -{ - return counter->Counter(); -} - -/** - * @return difference [ticks], taking rollover into account. - * (time-critical, so it's not called through ICounter.) - **/ -static inline u64 CounterDelta(u64 oldCounter, u64 newCounter) -{ - return (newCounter - oldCounter) & counterMask; -} - -double whrt_Resolution() -{ - ENSURE(resolution != 0.0); - return resolution; -} - - -//----------------------------------------------------------------------------- -// timer state - -// we're not going to bother calibrating the counter (i.e. measuring its -// current frequency by means of a second timer). rationale: -// - all counters except the TSC are stable and run at fixed frequencies; -// - it's not clear that any other HRT or the tick count would be useful -// as a stable time reference (if it were, we should be using it instead); -// - calibration would complicate the code (we'd have to make sure the -// secondary counter is safe and can co-exist with the primary). - -/** - * stores all timer state shared between readers and the update thread. - * (must be POD because it's used before static ctors run.) - **/ -struct TimerState -{ - // value of the counter at last update. - u64 counter; - - // total elapsed time [seconds] since first update. - // converted from tick deltas with the *then current* frequency - // (this enables calibration, which is currently not implemented, - // but leaving open the possibility costs nothing) - double time; - - u8 padding[48]; -}; - -// how do we detect when the old TimerState is no longer in use and can be -// freed? we use two static instances (avoids dynamic allocation headaches) -// and swap between them ('double-buffering'). it is assumed that all -// entered critical sections (the latching of TimerState fields) will have -// been exited before the next update comes around; if not, TimerState.time -// changes, the critical section notices and re-reads the new values. -static __declspec(align(64)) TimerState timerStates[2]; -// note: exchanging pointers is easier than XORing an index. -static volatile TimerState* volatile ts = &timerStates[0]; -static volatile TimerState* volatile ts2 = &timerStates[1]; - -static void UpdateTimerState() -{ - // how can we synchronize readers and the update thread? locks are - // preferably avoided since they're dangerous and can be slow. what we - // need to ensure is that TimerState doesn't change while another thread is - // accessing it. the first step is to linearize the update, i.e. have it - // appear to happen in an instant (done by building a new TimerState and - // having it go live by switching pointers). all that remains is to make - // reads of the state variables consistent, done by latching them all and - // retrying if an update came in the middle of this. - - const u64 currentCounter = Counter(); - const u64 deltaTicks = CounterDelta(ts->counter, currentCounter); - ts2->counter = currentCounter; - ts2->time = ts->time + deltaTicks/nominalFrequency; - ts = (volatile TimerState*)InterlockedExchangePointer((volatile PVOID*)&ts2, (PVOID)ts); -} - -double whrt_Time() -{ - // latch timer state (counter and time must be from the same update) - const volatile TimerState* state = ts; - return (state->time + CounterDelta(state->counter, Counter()) / nominalFrequency); -} - - -//----------------------------------------------------------------------------- -// update thread - -// note: we used to discipline the HRT timestamp to the system time, so it -// was advantageous to trigger updates via WinMM event (thus reducing -// instances where we're called in the middle of a scheduler tick). -// since that's no longer relevant, we prefer using a thread, because that -// avoids the dependency on WinMM and its lengthy startup time. - -// rationale: (+ and - are reasons for longer and shorter lengths) -// + minimize CPU usage -// + ensure all threads currently using TimerState return from those -// functions before the next interval -// - avoid more than 1 counter rollover per interval (InitUpdateThread makes -// sure our interval is shorter than the current counter's rollover rate) -static const DWORD UPDATE_INTERVAL_MS = 1000; - -static HANDLE hExitEvent; -static HANDLE hUpdateThread; - -static unsigned __stdcall UpdateThread(void* UNUSED(data)) -{ - debug_SetThreadName("whrt_UpdateThread"); - - for(;;) - { - const DWORD ret = WaitForSingleObject(hExitEvent, UPDATE_INTERVAL_MS); - // owner terminated or wait failed or exit event signaled - exit thread - if(ret != WAIT_TIMEOUT) - break; - - UpdateTimerState(); - } - - return 0; -} - -static inline Status InitUpdateThread() -{ - WinScopedPreserveLastError s; // CreateEvent - - // make sure our interval isn't too long - // (counterBits can be 64 => Bit() would overflow => calculate period/2) - const double period_2 = Bit(counterBits-1) / nominalFrequency; - const size_t rolloversPerInterval = size_t(UPDATE_INTERVAL_MS / i64(period_2*2.0*1000.0)); - ENSURE(rolloversPerInterval <= 1); - - hExitEvent = CreateEvent(0, TRUE, FALSE, 0); // manual reset, initially false - if(hExitEvent == INVALID_HANDLE_VALUE) - WARN_RETURN(ERR::LIMIT); - - hUpdateThread = (HANDLE)_beginthreadex(0, 0, UpdateThread, 0, 0, 0); - if(!hUpdateThread) - WARN_RETURN(ERR::LIMIT); - - return INFO::OK; -} - -static inline void ShutdownUpdateThread() -{ - // signal thread - BOOL ok = SetEvent(hExitEvent); - WARN_IF_FALSE(ok); - // the nice way is to wait for it to exit - if(WaitForSingleObject(hUpdateThread, 100) != WAIT_OBJECT_0) - TerminateThread(hUpdateThread, 0); // forcibly exit (dangerous) - CloseHandle(hExitEvent); - CloseHandle(hUpdateThread); -} - - -//----------------------------------------------------------------------------- - -static Status whrt_Init() -{ - InitCounter(); - - // latch initial counter value so that timer starts at 0 - ts->counter = Counter(); // must come before UpdateTimerState - - UpdateTimerState(); // must come before InitUpdateThread to avoid race - - RETURN_STATUS_IF_ERR(InitUpdateThread()); - - return INFO::OK; -} - - -static Status whrt_Shutdown() -{ - ShutdownUpdateThread(); - - ShutdownCounter(); - - acpi_Shutdown(); - - return INFO::OK; -} Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/whrt.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/hpet.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/hpet.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/hpet.h (nonexistent) @@ -1,33 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using High Precision Event Timer - */ - -#ifndef INCLUDED_HPET -#define INCLUDED_HPET - -class ICounter; -extern ICounter* CreateCounterHPET(void* address, size_t size); - -#endif // #ifndef INCLUDED_HPET Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/hpet.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/pmt.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/pmt.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/pmt.h (nonexistent) @@ -1,35 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using ACPI PM timer - */ - -#ifndef INCLUDED_PMT -#define INCLUDED_PMT - -static const i64 PMT_FREQ = 3579545; // (= master oscillator frequency/4) - -class ICounter; -extern ICounter* CreateCounterPMT(void* address, size_t size); - -#endif // #ifndef INCLUDED_PMT Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/pmt.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/qpc.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/qpc.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/qpc.h (nonexistent) @@ -1,33 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using QueryPerformanceCounter - */ - -#ifndef INCLUDED_QPC -#define INCLUDED_QPC - -class ICounter; -extern ICounter* CreateCounterQPC(void* address, size_t size); - -#endif // #ifndef INCLUDED_QPC Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/qpc.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/whrt.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/whrt.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/whrt.h (nonexistent) @@ -1,33 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Windows High Resolution Timer - */ - -#ifndef INCLUDED_WHRT -#define INCLUDED_WHRT - -extern double whrt_Resolution(); -extern double whrt_Time(); - -#endif // #ifndef INCLUDED_WHRT Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/whrt.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/whrt/tsc.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tsc.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/whrt/tsc.cpp (nonexistent) @@ -1,259 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Timer implementation using RDTSC - */ - -#include "precompiled.h" -#include "lib/sysdep/os/win/whrt/tsc.h" - -#include "lib/sysdep/os/win/whrt/counter.h" - -#include "lib/bits.h" -#include "lib/sysdep/acpi.h" -#include "lib/sysdep/os_cpu.h" -#include "lib/sysdep/os/win/win.h" -#include "lib/sysdep/os/win/wutil.h" - -#if ARCH_X86_X64 -# include "lib/sysdep/arch/x86_x64/x86_x64.h" // x86_x64::rdtsc -# include "lib/sysdep/arch/x86_x64/topology.h" -# include "lib/sysdep/arch/x86_x64/msr.h" -#endif - - -//----------------------------------------------------------------------------- - -static bool IsUniprocessor() -{ - if(topology::NumPackages() != 1) - return false; - if(topology::CoresPerPackage() != 1) - return false; - return true; -} - - -static bool IsInvariantTSC() -{ -#if ARCH_X86_X64 - // (we no longer need to check x86_x64::Vendor - Intel and AMD - // agreed on the definition of this feature check) - x86_x64::CpuidRegs regs = { 0 }; - regs.eax = 0x80000007; - if(x86_x64::cpuid(®s)) - { - // TSC is invariant across P-state, C-state, turbo, and - // stop grant transitions (e.g. STPCLK) - if(regs.edx & BIT(8)) - return true; - } -#endif - - return false; -} - - -static bool IsThrottlingPossible() -{ -#if ARCH_X86_X64 - x86_x64::CpuidRegs regs = { 0 }; - switch(x86_x64::Vendor()) - { - case x86_x64::VENDOR_INTEL: - if(x86_x64::Cap(x86_x64::CAP_TM_SCC) || x86_x64::Cap(x86_x64::CAP_EST)) - return true; - break; - - case x86_x64::VENDOR_AMD: - regs.eax = 0x80000007; - if(x86_x64::cpuid(®s)) - { - enum AmdPowerNowFlags - { - PN_FREQ_ID_CTRL = BIT(1), - PN_HW_THERMAL_CTRL = BIT(4), - PN_SW_THERMAL_CTRL = BIT(5) - }; - if(regs.edx & (PN_FREQ_ID_CTRL|PN_HW_THERMAL_CTRL|PN_SW_THERMAL_CTRL)) - return true; - } - break; - - default: - break; - } -#endif - - return false; -} - - -static bool IsSandyBridge() -{ - if(x86_x64::Vendor() != x86_x64::VENDOR_INTEL) - return false; - if(x86_x64::Model() == x86_x64::MODEL_SANDY_BRIDGE) - return true; - if(x86_x64::Model() == x86_x64::MODEL_SANDY_BRIDGE_2) - return true; - return false; -} - - -//----------------------------------------------------------------------------- - -class CounterTSC : public ICounter -{ -public: - virtual const char* Name() const - { - return "TSC"; - } - - Status Activate() - { -#if ARCH_X86_X64 - if(!x86_x64::Cap(x86_x64::CAP_TSC)) - return ERR::NOT_SUPPORTED; // NOWARN (CPU doesn't support RDTSC) -#endif - - return INFO::OK; - } - - void Shutdown() - { - } - - bool IsSafe() const - { - // using the TSC for timing is subject to a litany of - // potential problems, discussed below: - - if(IsInvariantTSC()) - return true; - - // SMP or multi-core => counters are unsynchronized. both offset and - // drift could be solved by maintaining separate per-core - // counter states, but that requires atomic reads of the TSC and - // the current processor number. - // - // (otherwise, we have a subtle race condition: if preempted while - // reading the time and rescheduled on a different core, incorrect - // results may be returned, which would be unacceptable.) - // - // unfortunately this isn't possible without OS support or the - // as yet unavailable RDTSCP instruction => unsafe. - // - // (note: if the TSC is invariant, drift is no longer a concern. - // we could synchronize the TSC MSRs during initialization and avoid - // per-core counter state and the race condition mentioned above. - // however, we won't bother, since such platforms aren't yet widespread - // and would surely support the nice and safe HPET, anyway) - if(!IsUniprocessor()) - return false; - - const FADT* fadt = (const FADT*)acpi_GetTable("FACP"); - if(fadt) - { - ENSURE(fadt->header.size >= sizeof(FADT)); - - // TSC isn't incremented in deep-sleep states => unsafe. - if(fadt->IsC3Supported()) - return false; - - // frequency throttling possible => unsafe. - if(fadt->IsDutyCycleSupported()) - return false; - } - -#if ARCH_X86_X64 - // recent CPU: - //if(x86_x64::Generation() >= 7) - { - // note: 8th generation CPUs support C1-clock ramping, which causes - // drift on multi-core systems, but those were excluded above. - - // in addition to frequency changes due to P-state transitions, - // we're also subject to STPCLK throttling. this happens when - // the chipset thinks the system is dangerously overheated; the - // OS isn't even notified. this may be rare, but could cause - // incorrect results => unsafe. - //return false; - } -#endif - - // we're dealing with a single older CPU; the only problem there is - // throttling, i.e. changes to the TSC frequency. we don't want to - // disable this because it may be important for cooling. the OS - // initiates changes but doesn't notify us; jumps are too frequent - // and drastic to detect and account for => unsafe. - if(IsThrottlingPossible()) - return false; - - return true; - } - - u64 Counter() const - { - return x86_x64::rdtsc(); - } - - size_t CounterBits() const - { - return 64; - } - - double NominalFrequency() const - { - // WARNING: do not call x86_x64::ClockFrequency because it uses the - // HRT, which we're currently in the process of initializing. - // instead query CPU clock frequency via OS. - // - // note: even here, initial accuracy isn't critical because the - // clock is subject to thermal drift and would require continual - // recalibration anyway. -#if ARCH_X86_X64 - if(MSR::IsAccessible() && MSR::HasPlatformInfo()) - { - const i64 busFrequency = IsSandyBridge()? 100000000 : 133333333; - const u64 platformInfo = MSR::Read(MSR::PLATFORM_INFO); - const u8 maxNonTurboRatio = bits(platformInfo, 8, 15); - return double(maxNonTurboRatio) * busFrequency; - } - else -#endif - return os_cpu_ClockFrequency(); - } - - double Resolution() const - { - return 1.0 / NominalFrequency(); - } -}; - -ICounter* CreateCounterTSC(void* address, size_t size) -{ - ENSURE(sizeof(CounterTSC) <= size); - return new(address) CounterTSC(); -} Property changes on: ps/trunk/source/lib/sysdep/os/win/whrt/tsc.cpp ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/source/lib/sysdep/os/win/mahaf.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/mahaf.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/mahaf.h (nonexistent) @@ -1,64 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * user-mode interface to Aken driver - */ - -// Mahaf - ferryman in Egyptian mythology that wakes up Aken, -// and the interface to the Aken driver. - -#ifndef INCLUDED_MAHAF -#define INCLUDED_MAHAF - -/** - * @return whether mapping physical memory is known to be dangerous - * on this platform. - * - * callable before or after mahaf_Init. - * - * note: mahaf_MapPhysicalMemory will complain if it - * is called despite this function having returned true. - **/ -LIB_API bool mahaf_IsPhysicalMappingDangerous(); - - -LIB_API Status mahaf_Init(); -LIB_API void mahaf_Shutdown(); - -LIB_API u8 mahaf_ReadPort8 (u16 port); -LIB_API u16 mahaf_ReadPort16(u16 port); -LIB_API u32 mahaf_ReadPort32(u16 port); -LIB_API void mahaf_WritePort8 (u16 port, u8 value); -LIB_API void mahaf_WritePort16(u16 port, u16 value); -LIB_API void mahaf_WritePort32(u16 port, u32 value); - -LIB_API volatile void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes); -LIB_API void mahaf_UnmapPhysicalMemory(volatile void* virtualAddress); - -LIB_API u64 mahaf_ReadModelSpecificRegister(u64 reg); -LIB_API void mahaf_WriteModelSpecificRegister(u64 reg, u64 value); - -// must be done in the driver because Windows clears CR4.PCE[8] -LIB_API u64 mahaf_ReadPerformanceMonitoringCounter(u64 reg); - -#endif // INCLUDED_MAHAF Property changes on: ps/trunk/source/lib/sysdep/os/win/mahaf.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: ps/trunk/binaries/system/readme.txt =================================================================== --- ps/trunk/binaries/system/readme.txt (revision 24136) +++ ps/trunk/binaries/system/readme.txt (revision 24137) @@ -1,108 +1,92 @@ COMMAND LINE OPTIONS Basic gameplay: -autostart=... load a map instead of showing main menu (see below) -editor launch the Atlas scenario editor -mod=NAME start the game using NAME mod -quickstart load faster (disables audio and some system info logging) Autostart: -autostart="TYPEDIR/MAPNAME" enables autostart and sets MAPNAME; TYPEDIR is skirmishes, scenarios, or random -autostart-seed=SEED sets randomization seed value (default 0, use -1 for random) -autostart-ai=PLAYER:AI sets the AI for PLAYER (e.g. 2:petra) -autostart-aidiff=PLAYER:DIFF sets the DIFFiculty of PLAYER's AI (0: sandbox, 5: very hard) -autostart-aiseed=AISEED sets the seed used for the AI random generator (default 0, use -1 for random) -autostart-player=NUMBER sets the playerID in non-networked games (default 1, use -1 for observer) -autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only) -autostart-team=PLAYER:TEAM sets the team for PLAYER (e.g. 2:2). -autostart-ceasefire=NUM sets a ceasefire duration NUM (default 0 minutes) -autostart-nonvisual disable any graphics and sounds -autostart-victory=SCRIPTNAME sets the victory conditions with SCRIPTNAME located in simulation/data/settings/victory_conditions/ (default conquest). When the first given SCRIPTNAME is "endless", no victory conditions will apply. -autostart-wonderduration=NUM sets the victory duration NUM for wonder victory condition (default 10 minutes) -autostart-relicduration=NUM sets the victory duration NUM for relic victory condition (default 10 minutes) -autostart-reliccount=NUM sets the number of relics for relic victory condition (default 2 relics) -autostart-disable-replay disable saving of replays Multiplayer: -autostart-playername=NAME sets local player NAME (default 'anonymous') -autostart-host sets multiplayer host mode -autostart-host-players=NUMBER sets NUMBER of human players for multiplayer game (default 2) -autostart-client=IP sets multiplayer client to join host at given IP address Random maps only: -autostart-size=TILES sets random map size in TILES (default 192) -autostart-players=NUMBER sets NUMBER of players on random map (default 2) Examples: 1) "Bob" will host a 2 player game on the Arcadia map: -autostart="scenarios/Arcadia" -autostart-host -autostart-host-players=2 -autostart-playername="Bob" "Alice" joins the match as player 2: -autostart="scenarios/Arcadia" -autostart-client=127.0.0.1 -autostart-playername="Alice" The players use the developer overlay to control players. 2) Load Alpine Lakes random map with random seed, 2 players (Athens and Britons), and player 2 is PetraBot: -autostart="random/alpine_lakes" -autostart-seed=-1 -autostart-players=2 -autostart-civ=1:athen -autostart-civ=2:brit -autostart-ai=2:petra 3) Observe the PetraBot on a triggerscript map: -autostart="random/jebel_barkal" -autostart-seed=-1 -autostart-players=2 -autostart-civ=1:athen -autostart-civ=2:brit -autostart-ai=1:petra -autostart-ai=2:petra -autostart-player=-1 RL client: -rl-interface Run the RL interface (see source/tools/rlclient) Configuration: -conf=KEY:VALUE set a config value -nosound disable audio -noUserMod disable loading of the user mod -shadows enable shadows -vsync enable VSync, i.e. lock FPS to monitor refresh rate -xres=N set screen X resolution to 'N' -yres=N set screen Y resolution to 'N' Installing mods: PATHS install mods located at PATHS. For instance: "./pyrogenesis mod1.pyromod mod2.zip" Advanced / diagnostic: -version print the version of the engine and exit -dumpSchema creates a file entity.rng in the working directory, containing complete entity XML schema, used by various analysis tools -entgraph (disabled) -listfiles (disabled) -profile=NAME (disabled) -replay=PATH non-visual replay of a previous game, used for analysis purposes PATH is system path to commands.txt containing simulation log -replay-visual=PATH visual replay of a previous game, used for analysis purposes PATH is system path to commands.txt containing simulation log -writableRoot store runtime game data in root data directory (only use if you have write permissions on that directory) -ooslog dumps simulation state in binary and ASCII representations each turn, files created in sim_log within the game's log folder. NOTE: game will run much slower with this option! -serializationtest checks simulation state each turn for serialization errors; on test failure, error is displayed and logs created in oos_log within the game's log folder. NOTE: game will run much slower with this option! -rejointest=N simulates a rejoin and checks simulation state each turn for serialization errors; this is similar to a serialization test but much faster and less complete. It should be enough for debugging most rejoin OOSes. -unique-logs adds unix timestamp and process id to the filename of mainlog.html, interestinglog.html and oos_dump.txt to prevent these files from becoming overwritten by another pyrogenesis process. -hashtest-full=X whether to enable computation of full hashes in replaymode (default true). Can be disabled to improve performance. -hashtest-quick=X whether to enable computation of quick hashes in replaymode (default false). Can be enabled for debugging purposes. -Windows-specific: --wQpcTscSafe allow timing via QueryPerformanceCounter despite the fact - that it's using TSC and it may be unsafe. has no effect if - a better timer (i.e. the HPET) is available. - should only be specified if: - - you are sure your system does not engage in - thermal throttling (including STPCLK) OR - - an "RDTSC patch" is installed - this flag is also useful if all other alternatives are worse - than a potentially risky or slightly broken TSC-based QPC. - --wNoMahaf prevent any physical memory mapping or direct port I/O. - this disables all ACPI-related code and thus some of the - timer backends. specify this if problems are observed with - one of the abovementioned subsystems. - Archive builder: -archivebuild=PATH system PATH of the base directory containing mod data to be archived/precached specify all mods it depends on with -mod=NAME -archivebuild-output=PATH system PATH to output of the resulting .zip archive (use with archivebuild) -archivebuild-compress enable deflate compression in the .zip (no zip compression by default since it hurts compression of release packages) Index: ps/trunk/source/lib/config2.h =================================================================== --- ps/trunk/source/lib/config2.h (revision 24136) +++ ps/trunk/source/lib/config2.h (revision 24137) @@ -1,110 +1,97 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * compile-time configuration for isolated spots */ #ifndef INCLUDED_CONFIG2 #define INCLUDED_CONFIG2 // rationale: a centralized header makes it much easier to see what all // can be changed. it is assumed that only a few modules will need // configuration choices, so rebuilding them all is acceptable. // use config.h when settings must apply to the entire project. // allow use of RDTSC for raw tick counts (otherwise, the slower but // more reliable on MP systems wall-clock will be used). #ifndef CONFIG2_TIMER_ALLOW_RDTSC # define CONFIG2_TIMER_ALLOW_RDTSC 1 #endif // this enables/disables the actual checking done by OverrunProtector // (quite slow, entailing mprotect() before/after each access). // define to 1 here or in the relevant module if you suspect mem corruption. // we provide this option because OverrunProtector requires some changes to // the object being wrapped, and we want to leave those intact but not // significantly slow things down except when needed. #ifndef CONFIG2_ALLOCATORS_OVERRUN_PROTECTION # define CONFIG2_ALLOCATORS_OVERRUN_PROTECTION 0 #endif // zero-copy IO means all clients share the cached buffer; changing their // contents is forbidden. this flag causes the buffers to be marked as // read-only via MMU (writes would cause an exception), which takes a // bit of extra time. #ifndef CONFIG2_CACHE_READ_ONLY #define CONFIG2_CACHE_READ_ONLY 1 #endif #ifndef CONFIG2_FILE_ENABLE_AIO // work around a bug introduced in Linux 2.6.38 // (http://www.wildfiregames.com/forum/index.php?showtopic=14561&view=findpost&p=217710) // OpenBSD doesn't provide aio.h so we disable its use # if OS_LINUX || OS_OPENBSD # define CONFIG2_FILE_ENABLE_AIO 0 # else # define CONFIG2_FILE_ENABLE_AIO 1 # endif #endif -// allow an attempt to start the Aken driver (i.e. service) at runtime. -// enable at your own risk on WinXP systems to allow access to -// better timers than Windows provides. on newer Windows versions, -// attempts to start the service from code fail unless the process -// is elevated, and definitely fail due to lack of cross-signing unless -// test-signing mode is active. -// if the user has taken explicit action to install and start the -// service via aken_install.bat, mahaf.cpp will be able to access it -// even if this is defined to 0. -#ifndef CONFIG2_MAHAF_ATTEMPT_DRIVER_START -# define CONFIG2_MAHAF_ATTEMPT_DRIVER_START 0 -#endif - // build in OpenGL ES 2.0 mode, instead of the default mode designed for // GL 1.1 + extensions. // this disables various features that are not supported by GLES. #ifndef CONFIG2_GLES # define CONFIG2_GLES 0 #endif // allow use of OpenAL/Ogg/Vorbis APIs #ifndef CONFIG2_AUDIO # define CONFIG2_AUDIO 1 #endif // allow use of NVTT #ifndef CONFIG2_NVTT # define CONFIG2_NVTT 1 #endif // allow use of lobby #ifndef CONFIG2_LOBBY # define CONFIG2_LOBBY 1 #endif // allow use of miniupnpc #ifndef CONFIG2_MINIUPNPC # define CONFIG2_MINIUPNPC 1 #endif #endif // #ifndef INCLUDED_CONFIG2 Index: ps/trunk/source/lib/sysdep/acpi.cpp =================================================================== --- ps/trunk/source/lib/sysdep/acpi.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/acpi.cpp (revision 24137) @@ -1,378 +1,170 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "precompiled.h" #include "lib/sysdep/acpi.h" #include "lib/byte_order.h" #include "lib/sysdep/cpu.h" #include "lib/module_init.h" -#define ENABLE_MAHAF 0 -#if ENABLE_MAHAF -# include "lib/sysdep/os/win/mahaf.h" -#else # include "lib/sysdep/os/win/wfirmware.h" -#endif #pragma pack(1) typedef const volatile u8* PCV_u8; typedef const volatile AcpiTable* PCV_AcpiTable; //----------------------------------------------------------------------------- // table static AcpiTable* AllocateTable(size_t size) { ENSURE(size >= sizeof(AcpiTable)); return (AcpiTable*)malloc(size); } template static void DeallocateTable(const T* table) { free((void*)table); } // return 8-bit checksum of a buffer (should be 0) static u8 ComputeChecksum(PCV_u8 buf, size_t numBytes) { // (can't use std::accumulate - we need 8-bit wraparound) u8 sum = 0; for(PCV_u8 p = buf; p < buf+numBytes; p++) sum = u8((sum + *p) & 0xFF); return sum; } static bool ValidateTable(const AcpiTable* table, const char* signature = 0) { if(!table) return false; // caller knowns the signature; make sure it matches if(signature) { if(memcmp(table->signature, signature, 4) != 0) return false; } // no specific signature is called for, just validate the characters. else { for(size_t i = 0; i < 4; i++) { const char c = table->signature[i]; // "ASF!" and "____" have been encountered if(!isalpha(c) && c != '_' && c != '!') return false; } } // must be at least as large as the common header if(table->size < sizeof(AcpiTable)) return false; // checksum of table must be 0 // .. AMIBIOS OEMB table has an incorrect checksum (off-by-one), // so don't complain about any OEM tables (ignored anyway). const bool isOemTable = (memcmp(table->signature, "OEM", 3) == 0); if(!isOemTable) { if(ComputeChecksum((PCV_u8)table, table->size) != 0) return false; } return true; } - -#if ENABLE_MAHAF - -//----------------------------------------------------------------------------- -// exception-safe transactional map/use/unmap - -// note: if the OS happens to unmap our physical memory, the Unsafe* -// functions may crash. we catch this via SEH; on Unix, we'd need handlers -// for SIGBUS and/or SIGSEGV. the code is safe in that it releases the -// mapped memory and returns an error code. - -static void* SUCCEEDED = (void*)(intptr_t)1; -static void* FAILED = (void*)(intptr_t)-1; - -typedef void* (*UnsafeFunction)(PCV_u8 mem, size_t numBytes, void* arg); - -static void* CallWithSafetyBlanket(UnsafeFunction func, PCV_u8 mem, size_t numBytes, void* arg) -{ -#if MSC_VERSION - __try - { - return func(mem, numBytes, arg); - } - __except(1) - { - return FAILED; - } -#else - return func(mem, numBytes, arg); -#endif -} - -static void* TransactPhysicalMemory(uintptr_t physicalAddress, size_t numBytes, UnsafeFunction func, void* arg = 0) -{ - PCV_u8 mem = (PCV_u8)mahaf_MapPhysicalMemory(physicalAddress, numBytes); - if(!mem) - return FAILED; - void* ret = CallWithSafetyBlanket(func, mem, numBytes, arg); - mahaf_UnmapPhysicalMemory((volatile void*)mem); - return ret; -} - - -//----------------------------------------------------------------------------- -// Root System Descriptor Pointer - -struct BiosDataArea -{ - u16 serialBase[4]; - u16 parallelBase[3]; - u16 ebdaSegment; -}; - -typedef const volatile BiosDataArea* PCV_BiosDataArea; - -static void* UnsafeReadEbdaPhysicalAddress(PCV_u8 mem, size_t numBytes, void* UNUSED(arg)) -{ - ENSURE(numBytes >= sizeof(BiosDataArea)); - - PCV_BiosDataArea bda = (PCV_BiosDataArea)mem; - const uintptr_t ebdaPhysicalAddress = ((uintptr_t)bda->ebdaSegment) * 16; - return (void*)ebdaPhysicalAddress; -} - - -struct RSDP -{ - char signature[8]; // "RSD PTR " - u8 checksum; // sum of this struct = 0 - char oemId[6]; - u8 revision; // 0 for 1.0, 2 for 2.0 - u32 rsdtPhysicalAddress; -}; - -typedef const volatile RSDP* PCV_RSDP; - -static const size_t RSDP_ALIGNMENT = 16; - -static void* UnsafeLocateAndRetrieveRsdp(PCV_u8 buf, size_t numBytes, void* arg) -{ - ENSURE(numBytes >= sizeof(RSDP)); - - for(PCV_u8 p = buf; p < buf+numBytes; p += RSDP_ALIGNMENT) - { - RSDP* prsdp = (RSDP*)p; - if(memcmp(prsdp->signature, "RSD PTR ", 8) != 0) - continue; - if(ComputeChecksum(p, sizeof(RSDP)) != 0) - continue; - - memcpy(arg, prsdp, sizeof(RSDP)); - return SUCCEEDED; - } - - return FAILED; -} - -static bool RetrieveRsdp(RSDP& rsdp) -{ - // See ACPIspec30b, section 5.2.5.1: - // RSDP is either in the first KIB of the extended BIOS data area, - void* ret = TransactPhysicalMemory(0x400, 0x100, UnsafeReadEbdaPhysicalAddress); - if(ret != FAILED) - { - const uintptr_t ebdaPhysicalAddress = (uintptr_t)ret; - ret = TransactPhysicalMemory(ebdaPhysicalAddress, 0x400, UnsafeLocateAndRetrieveRsdp, &rsdp); - if(ret == SUCCEEDED) - return true; - } - - // or in read-only BIOS memory. - ret = TransactPhysicalMemory(0xE0000, 0x20000, UnsafeLocateAndRetrieveRsdp, &rsdp); - if(ret == SUCCEEDED) - return true; - - return false; // not found -} - - -//----------------------------------------------------------------------------- -// copy tables from physical memory - -static void* UnsafeAllocateAndCopyTable(PCV_u8 mem, size_t numBytes, void* arg) -{ - ENSURE(numBytes >= sizeof(AcpiTable)); - - PCV_AcpiTable table = (PCV_AcpiTable)mem; - const size_t tableSize = table->size; - - // physical memory window is smaller than the table - // (caller will map a larger window and call us again) - if(numBytes < tableSize) - { - memcpy(arg, &tableSize, sizeof(size_t)); - return 0; - } - - PCV_u8 copy = (PCV_u8)AllocateTable(tableSize); - if(!copy) - return FAILED; - - memcpy((void*)copy, (const void*)mem, tableSize); - return (void*)copy; -} - - -static const AcpiTable* AllocateAndCopyTable(uintptr_t physicalAddress) -{ - // ACPI table sizes are not known until they've been mapped. since that - // is slow, we don't always want to do it twice. the solution is to map - // enough for a typical table; if that is too small, realloc and map again. - static const size_t initialSize = 4*KiB; - size_t actualSize = 0; - void* ret = TransactPhysicalMemory(physicalAddress, initialSize, UnsafeAllocateAndCopyTable, &actualSize); - // initialSize was too small; actualSize has been set - if(ret == 0) - ret = TransactPhysicalMemory(physicalAddress, actualSize, UnsafeAllocateAndCopyTable); - // *either* of the above calls failed to allocate memory - if(ret == FAILED) - return 0; - return (const AcpiTable*)ret; -} - -#endif // ENABLE_MAHAF - - static void AllocateAndCopyTables(const AcpiTable**& tables, size_t& numTables) { -#if ENABLE_MAHAF - if(mahaf_IsPhysicalMappingDangerous()) - return; - if(mahaf_Init() != INFO::OK) - return; - - RSDP rsdp; - if(!RetrieveRsdp(rsdp)) - return; - - // Root System Descriptor Table - struct RSDT - { - AcpiTable header; - u32 tableAddresses[1]; - }; - const RSDT* rsdt = (const RSDT*)AllocateAndCopyTable(rsdp.rsdtPhysicalAddress); - if(!ValidateTable(&rsdt->header, "RSDT")) - { - DeallocateTable(rsdt); - return; - } - - numTables = (rsdt->header.size - sizeof(AcpiTable)) / sizeof(rsdt->tableAddresses[0]); - ENSURE(numTables != 0); - - tables = new const AcpiTable*[numTables]; - for(size_t i = 0; i < numTables; i++) - tables[i] = AllocateAndCopyTable(rsdt->tableAddresses[i]); - - DeallocateTable(rsdt); -#else const wfirmware::Provider provider = FOURCC_BE('A','C','P','I'); const wfirmware::TableIds tableIDs = wfirmware::GetTableIDs(provider); numTables = tableIDs.size(); tables = new const AcpiTable*[numTables]; for(size_t i = 0; i < numTables; i++) { wfirmware::Table table = wfirmware::GetTable(provider, tableIDs[i]); ENSURE(!table.empty()); tables[i] = AllocateTable(table.size()); memcpy((void*)tables[i], &table[0], table.size()); } -#endif // to prevent callers from choking on invalid tables, we // zero out the corresponding tables[] entries. for(size_t i = 0; i < numTables; i++) { if(!ValidateTable(tables[i])) { DeallocateTable(tables[i]); tables[i] = 0; } } } //----------------------------------------------------------------------------- // note: avoid global std::map etc. because we may be called before _cinit static const AcpiTable** tables; // tables == 0 <=> not initialized static const AcpiTable* invalidTables; // tables == &invalidTables => init failed static size_t numTables; void acpi_Shutdown() { if(tables) { for(size_t i = 0; i < numTables; i++) DeallocateTable(tables[i]); SAFE_ARRAY_DELETE(tables); numTables = 0; } - -#if ENABLE_MAHAF - mahaf_Shutdown(); -#endif } const AcpiTable* acpi_GetTable(const char* signature) { if(cpu_CAS(&tables, (const AcpiTable**)0, &invalidTables)) AllocateAndCopyTables(tables, numTables); // (typically only a few tables, linear search is OK) for(size_t i = 0; i < numTables; i++) { const AcpiTable* table = tables[i]; if(!table) continue; // skip invalid tables, e.g. OEM (see above) if(strncmp(table->signature, signature, 4) == 0) return table; } return 0; // no matching AND valid table found } Index: ps/trunk/source/lib/sysdep/arch/x86_x64/apic.h =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/apic.h (revision 24136) +++ ps/trunk/source/lib/sysdep/arch/x86_x64/apic.h (revision 24137) @@ -1,49 +1,49 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INCLUDED_X86_X64_APIC #define INCLUDED_X86_X64_APIC typedef u8 ApicId; // not necessarily contiguous values /** * @return APIC ID of the currently executing processor or zero if the * platform does not have an xAPIC (i.e. 7th generation x86 or below). * * rationale: the alternative of accessing the APIC mmio registers is not - * feasible - mahaf_MapPhysicalMemory only works reliably on WinXP. we also - * don't want to interfere with the OS's constant use of the APIC registers. + * feasible. We also don't want to interfere with the OS's constant use of + * the APIC registers. **/ LIB_API ApicId GetApicId(); // if this returns false, apicId = contiguousId = processor. // otherwise, there are unspecified but bijective mappings between // apicId<->contiguousId and apicId<->processor. LIB_API bool AreApicIdsReliable(); LIB_API size_t ProcessorFromApicId(ApicId apicId); LIB_API size_t ContiguousIdFromApicId(ApicId apicId); LIB_API ApicId ApicIdFromProcessor(size_t contiguousId); LIB_API ApicId ApicIdFromContiguousId(size_t contiguousId); #endif // #ifndef INCLUDED_X86_X64_APIC Index: ps/trunk/source/lib/sysdep/os/win/wposix/wpthread.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wposix/wpthread.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/wposix/wpthread.cpp (revision 24137) @@ -1,706 +1,640 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * emulate pthreads on Windows. */ #include "precompiled.h" #include "lib/sysdep/os/win/wposix/wpthread.h" #include #include #include "lib/sysdep/cpu.h" // cpu_CAS #include "lib/posix/posix_filesystem.h" // O_CREAT #include "lib/sysdep/os/win/wposix/wposix_internal.h" #include "lib/sysdep/os/win/wposix/wtime.h" // timespec #include "lib/sysdep/os/win/wseh.h" // wseh_ExceptionFilter #include "lib/sysdep/os/win/winit.h" WINIT_REGISTER_CRITICAL_INIT(wpthread_Init); static HANDLE HANDLE_from_pthread(pthread_t p) { return (HANDLE)((char*)0 + p); } static pthread_t pthread_from_HANDLE(HANDLE h) { return (pthread_t)(uintptr_t)h; } //----------------------------------------------------------------------------- // misc //----------------------------------------------------------------------------- // non-pseudo handle so that pthread_self value is unique for each thread static __declspec(thread) HANDLE hCurrentThread; static void NotifyCurrentThread() { // (we leave it to the OS to clean these up at process exit - threads are not created often) WARN_IF_FALSE(DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hCurrentThread, 0, FALSE, DUPLICATE_SAME_ACCESS)); } int pthread_equal(pthread_t t1, pthread_t t2) { return t1 == t2; } pthread_t pthread_self() { return pthread_from_HANDLE(hCurrentThread); } int pthread_once(pthread_once_t* once, void (*init_routine)()) { if(cpu_CAS((volatile intptr_t*)once, 0, 1)) init_routine(); return 0; } int pthread_getschedparam(pthread_t thread, int* policy, struct sched_param* param) { if(policy) { DWORD pc = GetPriorityClass(GetCurrentProcess()); *policy = (pc >= HIGH_PRIORITY_CLASS)? SCHED_FIFO : SCHED_RR; } if(param) { const HANDLE hThread = HANDLE_from_pthread(thread); param->sched_priority = GetThreadPriority(hThread); } return 0; } int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param* param) { const int pri = param->sched_priority; // additional boost for policy == SCHED_FIFO DWORD pri_class = NORMAL_PRIORITY_CLASS; if(policy == SCHED_FIFO) { pri_class = HIGH_PRIORITY_CLASS; if(pri == 2) pri_class = REALTIME_PRIORITY_CLASS; } SetPriorityClass(GetCurrentProcess(), pri_class); // choose fixed Windows values from pri const HANDLE hThread = HANDLE_from_pthread(thread); SetThreadPriority(hThread, pri); return 0; } //----------------------------------------------------------------------------- // thread-local storage //----------------------------------------------------------------------------- // minimum amount of TLS slots every Windows version provides; // used to validate indices. static const size_t TLS_LIMIT = 64; // rationale: don't use an array of dtors for every possible TLS slot. // other DLLs may allocate any number of them in their DllMain, so the // array would have to be quite large. instead, store both key and dtor - // we are thus limited only by pthread_key_create calls (which we control). static const size_t MAX_DTORS = 4; static struct { pthread_key_t key; void (*dtor)(void*); } dtors[MAX_DTORS]; int pthread_key_create(pthread_key_t* key, void (*dtor)(void*)) { DWORD idx = TlsAlloc(); if(idx == TLS_OUT_OF_INDEXES) return -ENOMEM; ENSURE(idx < TLS_LIMIT); *key = (pthread_key_t)idx; // acquire a free dtor slot size_t i; for(i = 0; i < MAX_DTORS; i++) { if(cpu_CAS((volatile intptr_t*)&dtors[i].dtor, (intptr_t)0, (intptr_t)dtor)) goto have_slot; } // not enough slots; we have a valid key, but its dtor won't be called. WARN_IF_ERR(ERR::LIMIT); return -1; have_slot: dtors[i].key = *key; return 0; } int pthread_key_delete(pthread_key_t key) { DWORD idx = (DWORD)key; ENSURE(idx < TLS_LIMIT); BOOL ret = TlsFree(idx); ENSURE(ret != 0); return 0; } void* pthread_getspecific(pthread_key_t key) { DWORD idx = (DWORD)key; ENSURE(idx < TLS_LIMIT); // TlsGetValue sets last error to 0 on success (boo). // we don't want this to hide previous errors, so it's restored below. DWORD last_err = GetLastError(); void* data = TlsGetValue(idx); // no error if(GetLastError() == 0) { // we care about performance here. SetLastError is low overhead, // but last error = 0 is expected. if(last_err != 0) SetLastError(last_err); } else WARN_IF_ERR(ERR::FAIL); return data; } int pthread_setspecific(pthread_key_t key, const void* value) { DWORD idx = (DWORD)key; ENSURE(idx < TLS_LIMIT); BOOL ret = TlsSetValue(idx, (void*)value); ENSURE(ret != 0); return 0; } static void call_tls_dtors() { again: bool had_valid_tls = false; // for each registered dtor: (call order unspecified by SUSv3) for(size_t i = 0; i < MAX_DTORS; i++) { // is slot #i in use? void (*dtor)(void*) = dtors[i].dtor; if(!dtor) continue; // clear slot and call dtor with its previous value. const pthread_key_t key = dtors[i].key; void* tls = pthread_getspecific(key); if(tls) { WARN_IF_ERR(pthread_setspecific(key, 0)); dtor(tls); had_valid_tls = true; } } // rationale: SUSv3 says we're allowed to loop infinitely. we do so to // expose any dtor bugs - this shouldn't normally happen. if(had_valid_tls) goto again; } //----------------------------------------------------------------------------- // mutex //----------------------------------------------------------------------------- // rationale: CRITICAL_SECTIONS have less overhead than Win32 Mutex. // disadvantage is that pthread_mutex_timedlock isn't supported, but // the user can switch to semaphores if this facility is important. // DeleteCriticalSection currently doesn't complain if we double-free // (e.g. user calls destroy() and static initializer atexit runs), // and dox are ambiguous. // note: pthread_mutex_t must not be an opaque struct, because the // initializer returns pthread_mutex_t directly and CRITICAL_SECTIONS // shouldn't be copied. // // note: we use wutil_Allocate instead of new because the (no longer extant) // memory manager used a pthread_mutex. int pthread_mutexattr_init(pthread_mutexattr_t* UNUSED(attr)) { return 0; } int pthread_mutexattr_destroy(pthread_mutexattr_t* UNUSED(attr)) { return 0; } int pthread_mutexattr_gettype(const pthread_mutexattr_t* UNUSED(attr), int* type) { *type = PTHREAD_MUTEX_RECURSIVE; return 0; } int pthread_mutexattr_settype(pthread_mutexattr_t* UNUSED(attr), int type) { return (type == PTHREAD_MUTEX_RECURSIVE)? 0 : -ENOSYS; } static CRITICAL_SECTION* CRITICAL_SECTION_from_pthread_mutex_t(pthread_mutex_t* m) { if(!m) { DEBUG_WARN_ERR(ERR::LOGIC); return 0; } return (CRITICAL_SECTION*)*m; } pthread_mutex_t pthread_mutex_initializer() { CRITICAL_SECTION* cs = (CRITICAL_SECTION*)wutil_Allocate(sizeof(CRITICAL_SECTION)); InitializeCriticalSection(cs); return (pthread_mutex_t)cs; } int pthread_mutex_destroy(pthread_mutex_t* m) { CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m); if(!cs) return -1; DeleteCriticalSection(cs); wutil_Free(cs); *m = 0; // cause double-frees to be noticed return 0; } int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t*) { *m = pthread_mutex_initializer(); return 0; } int pthread_mutex_lock(pthread_mutex_t* m) { CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m); if(!cs) return -1; EnterCriticalSection(cs); return 0; } int pthread_mutex_trylock(pthread_mutex_t* m) { CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m); if(!cs) return -1; const BOOL successfullyEnteredOrAlreadyOwns = TryEnterCriticalSection(cs); return successfullyEnteredOrAlreadyOwns? 0 : -1; } int pthread_mutex_unlock(pthread_mutex_t* m) { CRITICAL_SECTION* cs = CRITICAL_SECTION_from_pthread_mutex_t(m); if(!cs) return -1; LeaveCriticalSection(cs); return 0; } // not implemented - pthread_mutex is based on CRITICAL_SECTION, // which doesn't support timeouts. use sem_timedwait instead. int pthread_mutex_timedlock(pthread_mutex_t* UNUSED(m), const struct timespec* UNUSED(abs_timeout)) { return -ENOSYS; } //----------------------------------------------------------------------------- // semaphore //----------------------------------------------------------------------------- static HANDLE HANDLE_from_sem_t(sem_t* sem) { return (HANDLE)*sem; } sem_t* sem_open(const char* name, int oflag, ...) { WinScopedPreserveLastError s; const bool create = (oflag & O_CREAT) != 0; const bool exclusive = (oflag & O_EXCL) != 0; // SUSv3 parameter requirements: ENSURE(name[0] == '/'); ENSURE((oflag & ~(O_CREAT|O_EXCL)) == 0); // no other bits ENSURE(!exclusive || create); // excl implies creat // if creating, get additional parameters unsigned initialValue = 0; if(create) { va_list args; va_start(args, oflag); const mode_t mode = va_arg(args, mode_t); initialValue = va_arg(args, unsigned); va_end(args); ENSURE(mode == 0700 && "this implementation ignores mode_t"); } // create or open SetLastError(0); const LONG maxValue = 0x7fffffff; const HANDLE hSemaphore = CreateSemaphore(0, (LONG)initialValue, maxValue, 0); if(hSemaphore == 0) return SEM_FAILED; const bool existed = (GetLastError() == ERROR_ALREADY_EXISTS); // caller insisted on creating anew, but it already existed if(exclusive && existed) { CloseHandle(hSemaphore); errno = EEXIST; return SEM_FAILED; } // caller wanted to open semaphore, but it didn't exist if(!create && !existed) { CloseHandle(hSemaphore); errno = ENOENT; return SEM_FAILED; } // we have to return a pointer to sem_t, and the sem_init interface // requires sem_t to be usable as the handle, so we'll have to // allocate memory here (ugh). sem_t* sem = new sem_t; *sem = (sem_t)hSemaphore; return sem; } int sem_close(sem_t* sem) { // jw: not sure if this is correct according to SUSv3, but it's // certainly enough for MessagePasserImpl's needs. sem_destroy(sem); delete sem; return 0; // success } int sem_unlink(const char* UNUSED(name)) { // see sem_close return 0; // success } int sem_init(sem_t* sem, int pshared, unsigned value) { SECURITY_ATTRIBUTES sec = { sizeof(SECURITY_ATTRIBUTES) }; sec.bInheritHandle = (BOOL)pshared; HANDLE h = CreateSemaphore(&sec, (LONG)value, 0x7fffffff, 0); WARN_IF_FALSE(h); *sem = (sem_t)h; return 0; } int sem_destroy(sem_t* sem) { HANDLE h = HANDLE_from_sem_t(sem); WARN_IF_FALSE(CloseHandle(h)); return 0; } int sem_post(sem_t* sem) { HANDLE h = HANDLE_from_sem_t(sem); WARN_IF_FALSE(ReleaseSemaphore(h, 1, 0)); return 0; } int sem_wait(sem_t* sem) { HANDLE h = HANDLE_from_sem_t(sem); DWORD ret = WaitForSingleObject(h, INFINITE); ENSURE(ret == WAIT_OBJECT_0); return 0; } -// helper function for sem_timedwait - multiple return is convenient. -// converts an absolute timeout deadline into a relative length for use with -// WaitForSingleObject with the following peculiarity: if the semaphore -// could be locked immediately, abs_timeout must be ignored (see SUS). -// to that end, we return a timeout of 0 and pass back = false if -// abs_timeout is invalid. -static DWORD calc_timeout_length_ms(const struct timespec* abs_timeout, - bool& timeout_is_valid) -{ - timeout_is_valid = false; - - if(!abs_timeout) - return 0; - - // SUS requires we fail if not normalized - if(abs_timeout->tv_nsec >= 1000000000) - return 0; - - struct timespec cur_time; - if(clock_gettime(CLOCK_REALTIME, &cur_time) != 0) - return 0; - - timeout_is_valid = true; - - // convert absolute deadline to relative length, rounding up to [ms]. - // note: use i64 to avoid overflow in multiply. - const i64 ds = abs_timeout->tv_sec - cur_time.tv_sec; - const long dn = abs_timeout->tv_nsec - cur_time.tv_nsec; - i64 length_ms = ds*1000 + (dn+500000)/1000000; - // .. deadline already reached; we'll still attempt to lock once - if(length_ms < 0) - return 0; - // .. length > 49 days => result won't fit in 32 bits. most likely bogus. - // note: we're careful to avoid returning exactly -1 since - // that's the Win32 INFINITE value. - if(length_ms >= 0xFFFFFFFF) - { - WARN_IF_ERR(ERR::LIMIT); - length_ms = 0xfffffffe; - } - return (DWORD)(length_ms & 0xFFFFFFFF); -} - -int sem_timedwait(sem_t* sem, const struct timespec* abs_timeout) -{ - bool timeout_is_valid; - DWORD timeout_ms = calc_timeout_length_ms(abs_timeout, timeout_is_valid); - - HANDLE h = HANDLE_from_sem_t(sem); - DWORD ret = WaitForSingleObject(h, timeout_ms); - // successfully decremented semaphore; bail. - if(ret == WAIT_OBJECT_0) - return 0; - - // we're going to return -1. decide what happened: - // .. abs_timeout was invalid (must not check this before trying to lock) - if(!timeout_is_valid) - errno = EINVAL; - // .. timeout reached (not a failure) - else if(ret == WAIT_TIMEOUT) - errno = ETIMEDOUT; - - return -1; -} - - // wait until semaphore is locked or a message arrives. non-portable. // // background: on Win32, UI threads must periodically pump messages, or // else deadlock may result (see WaitForSingleObject docs). that entails // avoiding any blocking functions. when event waiting is needed, // one cheap workaround would be to time out periodically and pump messages. // that would work, but either wastes CPU time waiting, or introduces // message latency. to avoid this, we provide an API similar to sem_wait and // sem_timedwait that gives MsgWaitForMultipleObjects functionality. // // return value: 0 if the semaphore has been locked (SUS terminology), // -1 otherwise. errno differentiates what happened: ETIMEDOUT if a // message arrived (this is to ease switching between message waiting and // periodic timeout), or an error indication. int sem_msgwait_np(sem_t* sem) { HANDLE h = HANDLE_from_sem_t(sem); DWORD ret = MsgWaitForMultipleObjects(1, &h, FALSE, INFINITE, QS_ALLEVENTS); // semaphore is signalled if(ret == WAIT_OBJECT_0) return 0; // something else: // .. message came up if(ret == WAIT_OBJECT_0+1) errno = ETIMEDOUT; // .. error else { errno = EINVAL; WARN_IF_ERR(ERR::FAIL); } return -1; } //----------------------------------------------------------------------------- // threads //----------------------------------------------------------------------------- // _beginthreadex cannot call the user's thread function directly due to // differences in calling convention; we need to pass its address and // the user-specified data pointer to our trampoline. // // rationale: // - a local variable in pthread_create isn't safe because the // new thread might not start before pthread_create returns. // - using one static FuncAndArg protected by critical section doesn't // work. wutil_Lock allows recursive locking, so if creating 2 threads, // the parent thread may create both without being stopped and thus // stomp on the first thread's func_and_arg. // - blocking pthread_create until the trampoline has latched func_and_arg // would work. this is a bit easier to understand than nonrecursive CS. // deadlock is impossible because thread_start allows the parent to // continue before doing anything dangerous. however, this requires // initializing a semaphore, which leads to init order problems. // - stashing func and arg in TLS would work, but it is a very limited // resource and __declspec(thread) is documented as possibly // interfering with delay loading. // - heap allocations are the obvious safe solution. we'd like to // minimize them, but it's the least painful way. struct FuncAndArg { void* (*func)(void*); void* arg; }; // bridge calling conventions required by _beginthreadex and POSIX. static unsigned __stdcall thread_start(void* param) { const FuncAndArg* func_and_arg = (const FuncAndArg*)param; void* (*func)(void*) = func_and_arg->func; void* arg = func_and_arg->arg; wutil_Free(param); NotifyCurrentThread(); void* ret = 0; __try { ret = func(arg); call_tls_dtors(); } __except(wseh_ExceptionFilter(GetExceptionInformation())) { ret = 0; } return (unsigned)(uintptr_t)ret; } int pthread_create(pthread_t* thread_id, const void* UNUSED(attr), void* (*func)(void*), void* arg) { // notes: // - use wutil_Allocate instead of the normal heap because we /might/ // potentially be called before _cinit. // - placement new is more trouble than it's worth // (see REDEFINED_NEW), so we don't bother with a ctor. FuncAndArg* func_and_arg = (FuncAndArg*)wutil_Allocate(sizeof(FuncAndArg)); if(!func_and_arg) { WARN_IF_ERR(ERR::NO_MEM); return -1; } func_and_arg->func = func; func_and_arg->arg = arg; // _beginthreadex has more overhead and no value added vs. // CreateThread, but it avoids small memory leaks in // ExitThread when using the statically-linked CRT (-> MSDN). HANDLE hThread = (HANDLE)_beginthreadex(0, 0, thread_start, func_and_arg, 0, 0); if(!hThread) { WARN_IF_ERR(ERR::FAIL); return -1; } // SUSv3 doesn't specify whether this is optional - go the safe route. if(thread_id) *thread_id = pthread_from_HANDLE(hThread); return 0; } int pthread_cancel(pthread_t thread) { HANDLE hThread = HANDLE_from_pthread(thread); TerminateThread(hThread, 0); debug_printf("WARNING: pthread_cancel is unsafe\n"); return 0; } int pthread_join(pthread_t thread, void** value_ptr) { HANDLE hThread = HANDLE_from_pthread(thread); // note: pthread_join doesn't call for a timeout. if this wait // locks up the process, at least it'll be easy to see why. DWORD ret = WaitForSingleObject(hThread, INFINITE); if(ret != WAIT_OBJECT_0) { WARN_IF_ERR(ERR::FAIL); return -1; } // pass back the code that was passed to pthread_exit. // SUS says <*value_ptr> need only be set on success! if(value_ptr) GetExitCodeThread(hThread, (LPDWORD)value_ptr); CloseHandle(hThread); return 0; } static Status wpthread_Init() { NotifyCurrentThread(); return INFO::OK; } Index: ps/trunk/source/lib/sysdep/os/win/wposix/wtime.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wposix/wtime.cpp (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/wposix/wtime.cpp (revision 24137) @@ -1,622 +1,586 @@ -/* Copyright (C) 2010 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * emulate POSIX time functionality on Windows. */ -// note: clock_gettime et al. have been removed. callers should use the -// WHRT directly, rather than needlessly translating s -> ns -> s, -// which costs time and accuracy. #include "precompiled.h" #include "lib/sysdep/os/win/wposix/wtime.h" #include "lib/sysdep/os/win/wposix/wposix_internal.h" -#include "lib/sysdep/os/win/whrt/whrt.h" WINIT_REGISTER_MAIN_INIT(wtime_Init); // whrt -> wtime // NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC. // SYSTEMTIME is a struct containing month, year, etc. static const long _1e3 = 1000; static const long _1e6 = 1000000; static const long _1e7 = 10000000; static const i64 _1e9 = 1000000000; // // FILETIME -> time_t routines; used by wposix filetime_to_time_t wrapper. // // hectonanoseconds between Windows and POSIX epoch static const u64 posix_epoch_hns = 0x019DB1DED53E8000; // this function avoids the pitfall of casting FILETIME* to u64*, // which is not safe due to differing alignment guarantees! // on some platforms, that would result in an exception. static u64 u64_from_FILETIME(const FILETIME* ft) { return u64_from_u32(ft->dwHighDateTime, ft->dwLowDateTime); } // convert UTC FILETIME to seconds-since-1970 UTC: // we just have to subtract POSIX epoch and scale down to units of seconds. // // used by wfilesystem. // // note: RtlTimeToSecondsSince1970 isn't officially documented, // so don't use that. time_t wtime_utc_filetime_to_time_t(FILETIME* ft) { u64 hns = u64_from_FILETIME(ft); u64 s = (hns - posix_epoch_hns) / _1e7; return (time_t)(s & 0xFFFFFFFF); } //----------------------------------------------------------------------------- // system clock at startup [nanoseconds since POSIX epoch] // note: the HRT starts at 0; any increase by the time we get here // just makes our notion of the start time more accurate) static i64 stInitial_ns; static void LatchInitialSystemTime() { FILETIME ft; GetSystemTimeAsFileTime(&ft); const u64 hns = u64_from_FILETIME(&ft); stInitial_ns = (hns - posix_epoch_hns) * 100; } -// return nanoseconds since POSIX epoch. -// algorithm: add current HRT value to the startup system time -static i64 CurrentSystemTime_ns() -{ - const i64 ns = stInitial_ns + i64(whrt_Time() * _1e9); - return ns; -} - static timespec TimespecFromNs(i64 ns) { timespec ts; ts.tv_sec = (time_t)((ns / _1e9) & 0xFFFFFFFF); ts.tv_nsec = (long)(ns % _1e9); return ts; } static size_t MsFromTimespec(const timespec& ts) { i64 ms = ts.tv_sec; // avoid overflow ms *= _1e3; ms += ts.tv_nsec / _1e6; return size_t(ms); } - -//----------------------------------------------------------------------------- - -int clock_gettime(clockid_t clock, struct timespec* ts) -{ - ENSURE(clock == CLOCK_REALTIME || clock == CLOCK_MONOTONIC); - - const i64 ns = CurrentSystemTime_ns(); - *ts = TimespecFromNs(ns); - return 0; -} - - -int clock_getres(clockid_t clock, struct timespec* ts) -{ - ENSURE(clock == CLOCK_REALTIME || clock == CLOCK_MONOTONIC); - - const double resolution = whrt_Resolution(); - const i64 ns = i64(resolution * 1e9); - *ts = TimespecFromNs(ns); - return 0; -} - - int nanosleep(const struct timespec* rqtp, struct timespec* /* rmtp */) { const DWORD ms = (DWORD)MsFromTimespec(*rqtp); if(ms) Sleep(ms); return 0; } unsigned sleep(unsigned sec) { // warn if overflow would result (it would be insane to ask for // such lengthy sleep timeouts, but still) ENSURE(sec < std::numeric_limits::max()/1000); const DWORD ms = sec * 1000; if(ms) Sleep(ms); return sec; } int usleep(useconds_t us) { ENSURE(us < _1e6); const DWORD ms = us/1000; if(ms) Sleep(ms); return 0; } //----------------------------------------------------------------------------- // strptime from NetBSD /* * Copyright (c) 1999 Kungliga Tekniska Hogskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of KTH nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ static const char *abb_weekdays[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL }; static const char *full_weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", NULL }; static const char *abb_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL }; static const char *full_month[] = { "January", "February", "Mars", "April", "May", "June", "July", "August", "September", "October", "November", "December", NULL, }; static const char *ampm[] = { "am", "pm", NULL }; /* * Try to match `*buf' to one of the strings in `strs'. Return the * index of the matching string (or -1 if none). Also advance buf. */ static int match_string (const char **buf, const char **strs) { int i = 0; for (i = 0; strs[i] != NULL; ++i) { size_t len = strlen (strs[i]); if (strncasecmp (*buf, strs[i], len) == 0) { *buf += len; return i; } } return -1; } /* * tm_year is relative this year */ const int tm_year_base = 1900; /* * Return TRUE iff `year' was a leap year. */ static int is_leap_year (int year) { return (year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0); } /* * Return the weekday [0,6] (0 = Sunday) of the first day of `year' */ static int first_day (int year) { int ret = 4; for (; year > 1970; --year) ret = (ret + 365 + is_leap_year (year) ? 1 : 0) % 7; return ret; } /* * Set `timeptr' given `wnum' (week number [0, 53]) */ static void set_week_number_sun (struct tm *timeptr, int wnum) { int fday = first_day (timeptr->tm_year + tm_year_base); timeptr->tm_yday = wnum * 7 + timeptr->tm_wday - fday; if (timeptr->tm_yday < 0) { timeptr->tm_wday = fday; timeptr->tm_yday = 0; } } /* * Set `timeptr' given `wnum' (week number [0, 53]) */ static void set_week_number_mon (struct tm *timeptr, int wnum) { int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7; timeptr->tm_yday = wnum * 7 + (timeptr->tm_wday + 6) % 7 - fday; if (timeptr->tm_yday < 0) { timeptr->tm_wday = (fday + 1) % 7; timeptr->tm_yday = 0; } } /* * Set `timeptr' given `wnum' (week number [0, 53]) */ static void set_week_number_mon4 (struct tm *timeptr, int wnum) { int fday = (first_day (timeptr->tm_year + tm_year_base) + 6) % 7; int offset = 0; if (fday < 4) offset += 7; timeptr->tm_yday = offset + (wnum - 1) * 7 + timeptr->tm_wday - fday; if (timeptr->tm_yday < 0) { timeptr->tm_wday = fday; timeptr->tm_yday = 0; } } /* * */ char * strptime (const char *buf, const char *format, struct tm *timeptr) { char c; for (; (c = *format) != '\0'; ++format) { char *s; int ret; if (isspace (c)) { while (isspace (*buf)) ++buf; } else if (c == '%' && format[1] != '\0') { c = *++format; if (c == 'E' || c == 'O') c = *++format; switch (c) { case 'A' : ret = match_string (&buf, full_weekdays); if (ret < 0) return NULL; timeptr->tm_wday = ret; break; case 'a' : ret = match_string (&buf, abb_weekdays); if (ret < 0) return NULL; timeptr->tm_wday = ret; break; case 'B' : ret = match_string (&buf, full_month); if (ret < 0) return NULL; timeptr->tm_mon = ret; break; case 'b' : case 'h' : ret = match_string (&buf, abb_month); if (ret < 0) return NULL; timeptr->tm_mon = ret; break; case 'C' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_year = (ret * 100) - tm_year_base; buf = s; break; case 'c' : abort (); case 'D' : /* %m/%d/%y */ s = strptime (buf, "%m/%d/%y", timeptr); if (s == NULL) return NULL; buf = s; break; case 'd' : case 'e' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_mday = ret; buf = s; break; case 'H' : case 'k' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_hour = ret; buf = s; break; case 'I' : case 'l' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; if (ret == 12) timeptr->tm_hour = 0; else timeptr->tm_hour = ret; buf = s; break; case 'j' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_yday = ret - 1; buf = s; break; case 'm' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_mon = ret - 1; buf = s; break; case 'M' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_min = ret; buf = s; break; case 'n' : if (*buf == '\n') ++buf; else return NULL; break; case 'p' : ret = match_string (&buf, ampm); if (ret < 0) return NULL; if (timeptr->tm_hour == 0) { if (ret == 1) timeptr->tm_hour = 12; } else timeptr->tm_hour += 12; break; case 'r' : /* %I:%M:%S %p */ s = strptime (buf, "%I:%M:%S %p", timeptr); if (s == NULL) return NULL; buf = s; break; case 'R' : /* %H:%M */ s = strptime (buf, "%H:%M", timeptr); if (s == NULL) return NULL; buf = s; break; case 'S' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_sec = ret; buf = s; break; case 't' : if (*buf == '\t') ++buf; else return NULL; break; case 'T' : /* %H:%M:%S */ case 'X' : s = strptime (buf, "%H:%M:%S", timeptr); if (s == NULL) return NULL; buf = s; break; case 'u' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_wday = ret - 1; buf = s; break; case 'w' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_wday = ret; buf = s; break; case 'U' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; set_week_number_sun (timeptr, ret); buf = s; break; case 'V' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; set_week_number_mon4 (timeptr, ret); buf = s; break; case 'W' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; set_week_number_mon (timeptr, ret); buf = s; break; case 'x' : s = strptime (buf, "%Y:%m:%d", timeptr); if (s == NULL) return NULL; buf = s; break; case 'y' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; if (ret < 70) timeptr->tm_year = 100 + ret; else timeptr->tm_year = ret; buf = s; break; case 'Y' : ret = strtol (buf, &s, 10); if (s == buf) return NULL; timeptr->tm_year = ret - tm_year_base; buf = s; break; case 'Z' : abort (); case '\0' : --format; /* FALLTHROUGH */ case '%' : if (*buf == '%') ++buf; else return NULL; break; default : if (*buf == '%' || *++buf == c) ++buf; else return NULL; break; } } else { if (*buf == c) ++buf; else return NULL; } } return (char *)buf; } //----------------------------------------------------------------------------- static Status wtime_Init() { LatchInitialSystemTime(); return INFO::OK; } Index: ps/trunk/source/lib/sysdep/os/win/wposix/wtime.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wposix/wtime.h (revision 24136) +++ ps/trunk/source/lib/sysdep/os/win/wposix/wtime.h (revision 24137) @@ -1,76 +1,55 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * emulate POSIX high resolution timer on Windows. */ #ifndef INCLUDED_WTIME #define INCLUDED_WTIME // // // typedef unsigned long useconds_t; typedef long suseconds_t; // // // LIB_API unsigned sleep(unsigned sec); LIB_API int usleep(useconds_t us); // // // -typedef enum -{ - // in our implementation, these actually do the same thing - // (a timer that latches the system time at startup and uses the - // monotonic HRT to add elapsed time since then) - CLOCK_REALTIME, - CLOCK_MONOTONIC -} -clockid_t; - -// POSIX realtime clock_* -#if _MSC_VER < 1900 -struct timespec -{ - time_t tv_sec; - long tv_nsec; -}; -#endif - extern int nanosleep(const struct timespec* rqtp, struct timespec* rmtp); -extern int clock_gettime(clockid_t clock, struct timespec* ts); -extern int clock_getres(clockid_t clock, struct timespec* res); LIB_API char* strptime(const char* buf, const char* format, struct tm* timeptr); #endif // #ifndef INCLUDED_WTIME Index: ps/trunk/source/lib/timer.cpp =================================================================== --- ps/trunk/source/lib/timer.cpp (revision 24136) +++ ps/trunk/source/lib/timer.cpp (revision 24137) @@ -1,229 +1,234 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * platform-independent high resolution timer */ #include "precompiled.h" #include "lib/timer.h" #include #include #include #include #include #include // std::stringstream #include "lib/module_init.h" #include "lib/posix/posix_time.h" #include "lib/sysdep/cpu.h" #if OS_WIN -# include "lib/sysdep/os/win/whrt/whrt.h" +#include "lib/sysdep/os/win/win.h" #endif #if OS_UNIX # include #endif #if OS_UNIX || OS_WIN # define HAVE_GETTIMEOFDAY 1 #else # define HAVE_GETTIMEOFDAY 0 #endif -#if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) || OS_WIN +#if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) # define HAVE_CLOCK_GETTIME 1 #else # define HAVE_CLOCK_GETTIME 0 #endif // rationale for wrapping gettimeofday and clock_gettime, instead of just // emulating them where not available: allows returning higher-resolution // timer values than their us / ns interface, via double [seconds]. // they're also not guaranteed to be monotonic. -#if HAVE_CLOCK_GETTIME +#if OS_WIN +static LARGE_INTEGER start; +#elif HAVE_CLOCK_GETTIME static struct timespec start; #elif HAVE_GETTIMEOFDAY static struct timeval start; #endif //----------------------------------------------------------------------------- // timer API void timer_LatchStartTime() { #if OS_WIN - // whrt_Time starts at zero, nothing needs to be done. + ENSURE(QueryPerformanceCounter(&start)); #elif HAVE_CLOCK_GETTIME (void)clock_gettime(CLOCK_REALTIME, &start); #elif HAVE_GETTIMEOFDAY gettimeofday(&start, 0); #endif } static std::mutex ensure_monotonic_mutex; // NB: does not guarantee strict monotonicity - callers must avoid // dividing by the difference of two equal times. static void EnsureMonotonic(double& newTime) { std::lock_guard lock(ensure_monotonic_mutex); static double maxTime; maxTime = std::max(maxTime, newTime); newTime = maxTime; } +// Cached because the default implementation may take several milliseconds. +static double resolution; double timer_Time() { double t; #if OS_WIN - t = whrt_Time(); + LARGE_INTEGER now; + ENSURE(QueryPerformanceCounter(&now)); + t = static_cast(now.QuadPart - start.QuadPart) * resolution; #elif HAVE_CLOCK_GETTIME ENSURE(start.tv_sec || start.tv_nsec); // must have called timer_LatchStartTime first struct timespec cur; (void)clock_gettime(CLOCK_REALTIME, &cur); t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec)*1e-9; #elif HAVE_GETTIMEOFDAY ENSURE(start.tv_sec || start.tv_usec); // must have called timer_LatchStartTime first struct timeval cur; gettimeofday(&cur, 0); t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6; #else # error "timer_Time: add timer implementation for this platform!" #endif EnsureMonotonic(t); return t; } -// cached because the default implementation may take several milliseconds -static double resolution; - static Status InitResolution() { #if OS_WIN - resolution = whrt_Resolution(); + LARGE_INTEGER frequency; + ENSURE(QueryPerformanceFrequency(&frequency)); + resolution = 1.0 / static_cast(frequency.QuadPart); #elif HAVE_CLOCK_GETTIME struct timespec ts; if(clock_getres(CLOCK_REALTIME, &ts) == 0) resolution = ts.tv_nsec * 1e-9; #else const double t0 = timer_Time(); double t1, t2; do t1 = timer_Time(); while(t1 == t0); do t2 = timer_Time(); while(t2 == t1); resolution = t2-t1; #endif return INFO::OK; } double timer_Resolution() { static ModuleInitState initState; ModuleInit(&initState, InitResolution); return resolution; } //----------------------------------------------------------------------------- // client API // intrusive linked-list of all clients. a fixed-size limit would be // acceptable (since timers are added manually), but the list is easy // to implement and only has the drawback of exposing TimerClient to users. // // do not use std::list et al. for this! we must be callable at any time, // especially before NLSO ctors run or before heap init. static size_t numClients; static TimerClient* clients; TimerClient* timer_AddClient(TimerClient* tc, const wchar_t* description) { tc->sum.SetToZero(); tc->description = description; // insert at front of list tc->next = clients; clients = tc; numClients++; return tc; } void timer_DisplayClientTotals() { debug_printf("TIMER TOTALS (%lu clients)\n", (unsigned long)numClients); debug_printf("-----------------------------------------------------\n"); for(TimerClient* tc = clients; tc; tc = tc->next) { const std::string duration = tc->sum.ToString(); debug_printf(" %s: %s (%lux)\n", utf8_from_wstring(tc->description).c_str(), duration.c_str(), (unsigned long)tc->num_calls); } debug_printf("-----------------------------------------------------\n"); } //----------------------------------------------------------------------------- std::string StringForSeconds(double seconds) { double scale = 1e6; const char* unit = " us"; if(seconds > 1.0) scale = 1, unit = " s"; else if(seconds > 1e-3) scale = 1e3, unit = " ms"; std::stringstream ss; ss << seconds*scale; ss << unit; return ss.str(); } std::string StringForCycles(Cycles cycles) { double scale = 1.0; const char* unit = " c"; if(cycles > 10000000000LL) // 10 Gc scale = 1e-9, unit = " Gc"; else if(cycles > 10000000) // 10 Mc scale = 1e-6, unit = " Mc"; else if(cycles > 10000) // 10 kc scale = 1e-3, unit = " kc"; std::stringstream ss; ss << cycles*scale; ss << unit; return ss.str(); }