Index: ps/trunk/binaries/system/readme.txt =================================================================== --- ps/trunk/binaries/system/readme.txt +++ ps/trunk/binaries/system/readme.txt @@ -84,22 +84,6 @@ -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 Index: ps/trunk/source/lib/config2.h =================================================================== --- ps/trunk/source/lib/config2.h +++ ps/trunk/source/lib/config2.h @@ -1,4 +1,4 @@ -/* 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 @@ -67,19 +67,6 @@ # 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. Index: ps/trunk/source/lib/sysdep/acpi.cpp =================================================================== --- ps/trunk/source/lib/sysdep/acpi.cpp +++ ps/trunk/source/lib/sysdep/acpi.cpp @@ -1,4 +1,4 @@ -/* 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 @@ -27,12 +27,7 @@ #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) @@ -108,206 +103,8 @@ 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); @@ -321,7 +118,6 @@ 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. @@ -352,10 +148,6 @@ SAFE_ARRAY_DELETE(tables); numTables = 0; } - -#if ENABLE_MAHAF - mahaf_Shutdown(); -#endif } Index: ps/trunk/source/lib/sysdep/arch/x86_x64/apic.h =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/apic.h +++ ps/trunk/source/lib/sysdep/arch/x86_x64/apic.h @@ -1,4 +1,4 @@ -/* 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 @@ -30,8 +30,8 @@ * 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(); Index: ps/trunk/source/lib/sysdep/arch/x86_x64/msr.h =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/msr.h +++ ps/trunk/source/lib/sysdep/arch/x86_x64/msr.h @@ -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 Index: ps/trunk/source/lib/sysdep/arch/x86_x64/msr.cpp =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/msr.cpp +++ ps/trunk/source/lib/sysdep/arch/x86_x64/msr.cpp @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/aken/aken.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/aken.h +++ ps/trunk/source/lib/sysdep/os/win/aken/aken.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/aken/aken.c =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/aken.c +++ ps/trunk/source/lib/sysdep/os/win/aken/aken.c @@ -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; -} Index: ps/trunk/source/lib/sysdep/os/win/aken/aken_install.bat =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/aken_install.bat +++ ps/trunk/source/lib/sysdep/os/win/aken/aken_install.bat @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/aken/build_all.bat =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/build_all.bat +++ ps/trunk/source/lib/sysdep/os/win/aken/build_all.bat @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/aken/build_single.bat =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/build_single.bat +++ ps/trunk/source/lib/sysdep/os/win/aken/build_single.bat @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/aken/makefile =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/makefile +++ ps/trunk/source/lib/sysdep/os/win/aken/makefile @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/aken/sources =================================================================== --- ps/trunk/source/lib/sysdep/os/win/aken/sources +++ ps/trunk/source/lib/sysdep/os/win/aken/sources @@ -1,5 +0,0 @@ -TARGETNAME=aken -TARGETPATH=. -TARGETTYPE=DRIVER - -SOURCES=aken.c Index: ps/trunk/source/lib/sysdep/os/win/mahaf.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/mahaf.h +++ ps/trunk/source/lib/sysdep/os/win/mahaf.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/mahaf.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/mahaf.cpp +++ ps/trunk/source/lib/sysdep/os/win/mahaf.cpp @@ -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); -} Index: ps/trunk/source/lib/sysdep/os/win/whrt/counter.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/counter.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/counter.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/counter.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/counter.cpp +++ ps/trunk/source/lib/sysdep/os/win/whrt/counter.cpp @@ -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; -} Index: ps/trunk/source/lib/sysdep/os/win/whrt/hpet.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/hpet.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/hpet.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/hpet.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/hpet.cpp +++ ps/trunk/source/lib/sysdep/os/win/whrt/hpet.cpp @@ -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(); -} Index: ps/trunk/source/lib/sysdep/os/win/whrt/pit.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/pit.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/pit.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/pmt.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/pmt.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/pmt.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/pmt.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/pmt.cpp +++ ps/trunk/source/lib/sysdep/os/win/whrt/pmt.cpp @@ -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(); -} Index: ps/trunk/source/lib/sysdep/os/win/whrt/qpc.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/qpc.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/qpc.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/qpc.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/qpc.cpp +++ ps/trunk/source/lib/sysdep/os/win/whrt/qpc.cpp @@ -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(); -} Index: ps/trunk/source/lib/sysdep/os/win/whrt/tgt.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tgt.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/tgt.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/tgt.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tgt.cpp +++ ps/trunk/source/lib/sysdep/os/win/whrt/tgt.cpp @@ -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(); -} Index: ps/trunk/source/lib/sysdep/os/win/whrt/tsc.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tsc.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/tsc.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/tsc.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/tsc.cpp +++ ps/trunk/source/lib/sysdep/os/win/whrt/tsc.cpp @@ -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(); -} Index: ps/trunk/source/lib/sysdep/os/win/whrt/whrt.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/whrt.h +++ ps/trunk/source/lib/sysdep/os/win/whrt/whrt.h @@ -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 Index: ps/trunk/source/lib/sysdep/os/win/whrt/whrt.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/whrt/whrt.cpp +++ ps/trunk/source/lib/sysdep/os/win/whrt/whrt.cpp @@ -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; -} Index: ps/trunk/source/lib/sysdep/os/win/wposix/wpthread.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wposix/wpthread.cpp +++ ps/trunk/source/lib/sysdep/os/win/wposix/wpthread.cpp @@ -1,4 +1,4 @@ -/* 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 @@ -474,72 +474,6 @@ } -// 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 Index: ps/trunk/source/lib/sysdep/os/win/wposix/wtime.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wposix/wtime.h +++ ps/trunk/source/lib/sysdep/os/win/wposix/wtime.h @@ -1,4 +1,4 @@ -/* 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 @@ -48,28 +48,7 @@ // // -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); Index: ps/trunk/source/lib/sysdep/os/win/wposix/wtime.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wposix/wtime.cpp +++ ps/trunk/source/lib/sysdep/os/win/wposix/wtime.cpp @@ -1,4 +1,4 @@ -/* 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 @@ -24,15 +24,11 @@ * 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 @@ -91,14 +87,6 @@ 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; @@ -115,30 +103,6 @@ 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); Index: ps/trunk/source/lib/timer.cpp =================================================================== --- ps/trunk/source/lib/timer.cpp +++ ps/trunk/source/lib/timer.cpp @@ -1,4 +1,4 @@ -/* 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 @@ -39,7 +39,7 @@ #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 @@ -51,7 +51,7 @@ # 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 @@ -62,7 +62,9 @@ // 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; @@ -75,7 +77,7 @@ 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 @@ -94,13 +96,17 @@ 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; @@ -120,13 +126,12 @@ } -// 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)