1
0
forked from 0ad/0ad
0ad/source/lib/sysdep/os/win/mahaf.cpp

379 lines
9.5 KiB
C++

/* 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/module_init.h"
#include "lib/sysdep/os/win/wutil.h"
#include <winioctl.h>
#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
//-----------------------------------------------------------------------------
static SC_HANDLE OpenServiceControlManager()
{
LPCWSTR machineName = 0; // local
LPCWSTR databaseName = 0; // default
SC_HANDLE hSCM = OpenSCManagerW(machineName, databaseName, SC_MANAGER_ALL_ACCESS);
if(!hSCM)
{
// administrator privileges are required. note: installing the
// service and having it start automatically would allow
// Least-Permission accounts to use it, but is too invasive and
// thus out of the question.
// rule out other problems
ENSURE(GetLastError() == ERROR_ACCESS_DENIED);
return 0;
}
return hSCM; // success
}
static void UninstallDriver()
{
SC_HANDLE hSCM = OpenServiceControlManager();
if(!hSCM)
return;
SC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_ALL_ACCESS);
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);
}
static void StartDriver(const OsPath& driverPathname)
{
const SC_HANDLE hSCM = OpenServiceControlManager();
if(!hSCM)
{
ENSURE(GetLastError() == ERROR_ACCESS_DENIED);
SetLastError(0);
return;
}
SC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_ALL_ACCESS);
// 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_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
OsString(driverPathname).c_str(), 0, 0, 0, startName, 0);
ENSURE(hService != 0);
}
// start service
{
DWORD numArgs = 0;
BOOL ok = StartService(hService, numArgs, 0);
if(!ok)
{
if(GetLastError() != ERROR_SERVICE_ALREADY_RUNNING)
{
// starting failed. don't raise a warning because this
// always happens on least-permission user accounts.
//DEBUG_WARN_ERR(ERR::LOGIC);
}
}
}
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;
}
//-----------------------------------------------------------------------------
static Status Init()
{
WinScopedPreserveLastError s;
if(wutil_HasCommandLineArgument(L"-wNoMahaf"))
return ERR::NOT_SUPPORTED; // NOWARN
{
const OsPath driverPathname = DriverPathname();
StartDriver(driverPathname);
}
{
const DWORD shareMode = 0;
hAken = CreateFileW(L"\\\\.\\Aken", GENERIC_READ, shareMode, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hAken == INVALID_HANDLE_VALUE)
{
ENSURE(GetLastError() == ERROR_FILE_NOT_FOUND);
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);
}