Index: binaries/system/readme.txt =================================================================== --- binaries/system/readme.txt +++ binaries/system/readme.txt @@ -93,11 +93,6 @@ 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: source/lib/config2.h =================================================================== --- source/lib/config2.h +++ source/lib/config2.h @@ -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: source/lib/sysdep/acpi.cpp =================================================================== --- source/lib/sysdep/acpi.cpp +++ 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: source/lib/sysdep/arch/x86_x64/apic.h =================================================================== --- source/lib/sysdep/arch/x86_x64/apic.h +++ source/lib/sysdep/arch/x86_x64/apic.h @@ -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: source/lib/sysdep/arch/x86_x64/msr.h =================================================================== --- source/lib/sysdep/arch/x86_x64/msr.h +++ /dev/null @@ -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: source/lib/sysdep/arch/x86_x64/msr.cpp =================================================================== --- source/lib/sysdep/arch/x86_x64/msr.cpp +++ /dev/null @@ -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: source/lib/sysdep/os/win/aken/aken.h =================================================================== --- source/lib/sysdep/os/win/aken/aken.h +++ 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: source/lib/sysdep/os/win/aken/aken.c =================================================================== --- source/lib/sysdep/os/win/aken/aken.c +++ 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: source/lib/sysdep/os/win/aken/aken_install.bat =================================================================== --- source/lib/sysdep/os/win/aken/aken_install.bat +++ 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: source/lib/sysdep/os/win/aken/build_all.bat =================================================================== --- source/lib/sysdep/os/win/aken/build_all.bat +++ 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: source/lib/sysdep/os/win/aken/build_single.bat =================================================================== --- source/lib/sysdep/os/win/aken/build_single.bat +++ 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: source/lib/sysdep/os/win/aken/makefile =================================================================== --- source/lib/sysdep/os/win/aken/makefile +++ 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: source/lib/sysdep/os/win/aken/sources =================================================================== --- source/lib/sysdep/os/win/aken/sources +++ source/lib/sysdep/os/win/aken/sources @@ -1,5 +0,0 @@ -TARGETNAME=aken -TARGETPATH=. -TARGETTYPE=DRIVER - -SOURCES=aken.c Index: source/lib/sysdep/os/win/mahaf.h =================================================================== --- source/lib/sysdep/os/win/mahaf.h +++ /dev/null @@ -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: source/lib/sysdep/os/win/mahaf.cpp =================================================================== --- source/lib/sysdep/os/win/mahaf.cpp +++ /dev/null @@ -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: source/lib/sysdep/os/win/whrt/counter.cpp =================================================================== --- source/lib/sysdep/os/win/whrt/counter.cpp +++ source/lib/sysdep/os/win/whrt/counter.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 @@ -30,10 +30,8 @@ #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/tsc.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. @@ -54,22 +52,17 @@ // 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. + // - QPC is used by most games engines and is more stable than the others. + // - TSC is buggy on some machines like the AMD Zen 2 CPUs. // - 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); + return CreateCounterQPC(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; Index: source/lib/sysdep/os/win/whrt/hpet.h =================================================================== --- source/lib/sysdep/os/win/whrt/hpet.h +++ /dev/null @@ -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: source/lib/sysdep/os/win/whrt/hpet.cpp =================================================================== --- source/lib/sysdep/os/win/whrt/hpet.cpp +++ /dev/null @@ -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: source/lib/sysdep/os/win/whrt/pmt.h =================================================================== --- source/lib/sysdep/os/win/whrt/pmt.h +++ /dev/null @@ -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: source/lib/sysdep/os/win/whrt/pmt.cpp =================================================================== --- source/lib/sysdep/os/win/whrt/pmt.cpp +++ /dev/null @@ -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: source/lib/sysdep/os/win/whrt/qpc.cpp =================================================================== --- source/lib/sysdep/os/win/whrt/qpc.cpp +++ source/lib/sysdep/os/win/whrt/qpc.cpp @@ -33,8 +33,8 @@ #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 +static const i64 PMT_FREQ = 3579545; class CounterQPC : public ICounter { @@ -102,10 +102,7 @@ // 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; - } + return wutil_HasCommandLineArgument(L"-wQpcTscSafe"); // 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 Index: source/lib/sysdep/os/win/whrt/tsc.cpp =================================================================== --- source/lib/sysdep/os/win/whrt/tsc.cpp +++ source/lib/sysdep/os/win/whrt/tsc.cpp @@ -38,7 +38,6 @@ #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 @@ -187,22 +186,6 @@ 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 @@ -226,23 +209,6 @@ 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(); }