Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
mahaf.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * user-mode interface to Aken driver
25  */
26 
27 #include "precompiled.h"
29 
30 #include "lib/config2.h"
31 #include "lib/module_init.h"
32 
34 #include <winioctl.h>
37 
38 static HANDLE hAken = INVALID_HANDLE_VALUE; // handle to Aken driver
39 
40 
41 //-----------------------------------------------------------------------------
42 // ioctl wrappers
43 //-----------------------------------------------------------------------------
44 
45 static u32 ReadPort(u16 port, u8 numBytes)
46 {
47  AkenReadPortIn in;
48  in.port = (USHORT)port;
49  in.numBytes = (UCHAR)numBytes;
51 
52  DWORD bytesReturned;
53  LPOVERLAPPED ovl = 0; // synchronous
54  const BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_READ_PORT, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl);
56 
57  ENSURE(bytesReturned == sizeof(out));
58  return out.value;
59 }
60 
62 {
63  const u32 value = ReadPort(port, 1);
64  ENSURE(value <= 0xFF);
65  return (u8)(value & 0xFF);
66 }
67 
69 {
70  const u32 value = ReadPort(port, 2);
71  ENSURE(value <= 0xFFFF);
72  return (u16)(value & 0xFFFF);
73 }
74 
76 {
77  const u32 value = ReadPort(port, 4);
78  return value;
79 }
80 
81 
82 static void WritePort(u16 port, u32 value, u8 numBytes)
83 {
84  AkenWritePortIn in;
85  in.value = (DWORD32)value;
86  in.port = (USHORT)port;
87  in.numBytes = (UCHAR)numBytes;
88 
89  DWORD bytesReturned; // unused but must be passed to DeviceIoControl
90  LPOVERLAPPED ovl = 0; // synchronous
91  BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_WRITE_PORT, &in, sizeof(in), 0, 0u, &bytesReturned, ovl);
92  WARN_IF_FALSE(ok);
93 }
94 
95 void mahaf_WritePort8(u16 port, u8 value)
96 {
97  WritePort(port, (u32)value, 1);
98 }
99 
100 void mahaf_WritePort16(u16 port, u16 value)
101 {
102  WritePort(port, (u32)value, 2);
103 }
104 
105 void mahaf_WritePort32(u16 port, u32 value)
106 {
107  WritePort(port, value, 4);
108 }
109 
110 
112 {
113  // pre-XP versions don't prevent re-mapping pages with incompatible
114  // attributes, which may lead to disaster due to TLB corruption.
116  return true;
117 
118  return false;
119 }
120 
121 
122 volatile void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)
123 {
125 
126  AkenMapIn in;
127  in.physicalAddress = (DWORD64)physicalAddress;
128  in.numBytes = (DWORD64)numBytes;
129  AkenMapOut out;
130 
131  DWORD bytesReturned;
132  LPOVERLAPPED ovl = 0; // synchronous
133  const BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_MAP, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl);
135 
136  ENSURE(bytesReturned == sizeof(out));
137  volatile void* virtualAddress = (volatile void*)(uintptr_t)out.virtualAddress;
138  return virtualAddress;
139 }
140 
141 
142 void mahaf_UnmapPhysicalMemory(volatile void* virtualAddress)
143 {
145 
146  AkenUnmapIn in;
147  in.virtualAddress = (DWORD64)virtualAddress;
148 
149  DWORD bytesReturned; // unused but must be passed to DeviceIoControl
150  LPOVERLAPPED ovl = 0; // synchronous
151  BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_UNMAP, &in, sizeof(in), 0, 0u, &bytesReturned, ovl);
152  WARN_IF_FALSE(ok);
153 }
154 
155 
156 static u64 ReadRegister(DWORD ioctl, u64 reg)
157 {
159  in.reg = reg;
161 
162  DWORD bytesReturned;
163  LPOVERLAPPED ovl = 0; // synchronous
164  const BOOL ok = DeviceIoControl(hAken, ioctl, &in, sizeof(in), &out, sizeof(out), &bytesReturned, ovl);
166 
167  ENSURE(bytesReturned == sizeof(out));
168  return out.value;
169 }
170 
172 {
173  return ReadRegister((DWORD)IOCTL_AKEN_READ_MSR, reg);
174 }
175 
177 {
178  return ReadRegister((DWORD)IOCTL_AKEN_READ_PMC, reg);
179 }
180 
182 {
184  in.reg = reg;
185  in.value = value;
186 
187  DWORD bytesReturned; // unused but must be passed to DeviceIoControl
188  LPOVERLAPPED ovl = 0; // synchronous
189  BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_WRITE_MSR, &in, sizeof(in), 0, 0u, &bytesReturned, ovl);
190  WARN_IF_FALSE(ok);
191 }
192 
193 
194 //-----------------------------------------------------------------------------
195 // driver installation
196 //-----------------------------------------------------------------------------
197 
198 // @param access need not include SC_MANAGER_CONNECT ("implicitly specified")
199 static SC_HANDLE OpenServiceControlManager(DWORD access)
200 {
201  LPCWSTR machineName = 0; // local
202  LPCWSTR databaseName = 0; // default
203  SC_HANDLE hSCM = OpenSCManagerW(machineName, databaseName, access);
204  if(!hSCM)
205  {
206  // ensure no other problems arose
207  ENSURE(GetLastError() == ERROR_ACCESS_DENIED);
208 
209  // administrator privileges are required for SC_MANAGER_CREATE_SERVICE.
210  // this is a problem on Vista / Win7, so users will have to use the
211  // separate aken_install.bat
212 
213  return 0;
214  }
215 
216  return hSCM; // success
217 }
218 
219 
220 static void UninstallDriver()
221 {
222  SC_HANDLE hSCM = OpenServiceControlManager(SC_MANAGER_ENUMERATE_SERVICE);
223  if(!hSCM)
224  return;
225  SC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_STOP|SERVICE_INTERROGATE);
226  if(!hService)
227  return;
228 
229  // stop service
230  SERVICE_STATUS serviceStatus;
231  if(!ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus))
232  {
233  // if the problem wasn't that the service is already stopped,
234  // something actually went wrong.
235  const DWORD err = GetLastError();
236  ENSURE(err == ERROR_SERVICE_NOT_ACTIVE || err == ERROR_SERVICE_CANNOT_ACCEPT_CTRL);
237  }
238 
239  // delete service
240  BOOL ok;
241  ok = DeleteService(hService);
242  WARN_IF_FALSE(ok);
243  ok = CloseServiceHandle(hService);
244  WARN_IF_FALSE(ok);
245 
246  ok = CloseServiceHandle(hSCM);
247  WARN_IF_FALSE(ok);
248 }
249 
250 
251 #if CONFIG2_MAHAF_ATTEMPT_DRIVER_START
252 
253 static void StartDriver(const OsPath& driverPathname)
254 {
255  const SC_HANDLE hSCM = OpenServiceControlManager(SC_MANAGER_CREATE_SERVICE);
256  if(!hSCM)
257  {
258  ENSURE(GetLastError() == ERROR_ACCESS_DENIED);
259  SetLastError(0);
260  return;
261  }
262 
263  SC_HANDLE hService = OpenServiceW(hSCM, AKEN_NAME, SERVICE_START);
264 
265  // during development, we want to ensure the newest build is used, so
266  // unload and re-create the service if it's running/installed.
267  // as of 2008-03-24 no further changes to Aken are pending, so this is
268  // disabled (thus also avoiding trouble when running multiple instances)
269 #if 0
270  if(hService)
271  {
272  BOOL ok = CloseServiceHandle(hService);
273  WARN_IF_FALSE(ok);
274  hService = 0;
275  UninstallDriver();
276  }
277 #endif
278 
279  // create service (note: this just enters the service into SCM's DB;
280  // no error is raised if the driver binary doesn't exist etc.)
281  if(!hService)
282  {
283  LPCWSTR startName = 0; // LocalSystem
284  // NB: Windows 7 seems to insist upon backslashes (i.e. external_file_string)
285  hService = CreateServiceW(hSCM, AKEN_NAME, AKEN_NAME,
286  SERVICE_START, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
287  OsString(driverPathname).c_str(), 0, 0, 0, startName, 0);
288  ENSURE(hService != 0);
289  }
290 
291  {
292  DWORD numArgs = 0;
293  BOOL ok = StartService(hService, numArgs, 0);
294  if(!ok)
295  {
296  switch(GetLastError())
297  {
298  case ERROR_SERVICE_ALREADY_RUNNING:
299  // ok, no action needed
300  break;
301  case ERROR_ACCESS_DENIED:
302  // Win7, can't start service; must use aken_install.bat
303  break;
304  case ERROR_INVALID_IMAGE_HASH:
305  // Win7 x86 rejects our code signing certificate; must enable
306  // "test signing" mode via aken_install.bat
307  break;
308  default:
309  // unexpected problem
311  break;
312  }
313  }
314  }
315 
316  CloseServiceHandle(hService);
317  CloseServiceHandle(hSCM);
318 }
319 
320 
321 static bool Is64BitOs()
322 {
323 #if OS_WIN64
324  return true;
325 #else
326  return wutil_IsWow64();
327 #endif
328 }
329 
330 static OsPath DriverPathname()
331 {
332  const char* const bits = Is64BitOs()? "64" : "";
333 #ifdef NDEBUG
334  const char* const debug = "";
335 #else
336  const char* const debug = "d";
337 #endif
338  char filename[PATH_MAX];
339  sprintf_s(filename, ARRAY_SIZE(filename), "aken%s%s.sys", bits, debug);
340  return wutil_ExecutablePath() / filename;
341 }
342 
343 #endif // CONFIG2_MAHAF_ATTEMPT_DRIVER_START
344 
345 
346 //-----------------------------------------------------------------------------
347 
348 static Status Init()
349 {
351 
352  if(wutil_HasCommandLineArgument(L"-wNoMahaf"))
353  return ERR::NOT_SUPPORTED; // NOWARN
354 
355 #if CONFIG2_MAHAF_ATTEMPT_DRIVER_START
356  {
357  const OsPath driverPathname = DriverPathname();
358  StartDriver(driverPathname);
359  }
360 #endif
361 
362  {
363  const DWORD shareMode = 0;
364  hAken = CreateFileW(L"\\\\.\\Aken", GENERIC_READ, shareMode, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
365  if(hAken == INVALID_HANDLE_VALUE)
366  {
367  // GetLastError() is ERROR_FILE_NOT_FOUND if the driver isn't running,
368  // but this is also reached when a handle has already been opened
369  // (e.g. by a second instance of the same program) - in which case we must
370  // indicate failure so that clients won't engage in unsynchronized ring 0 operations.
371  SetLastError(0);
372  return ERR::INVALID_HANDLE; // NOWARN (happens often due to security restrictions)
373  }
374  }
375 
376  return INFO::OK;
377 }
378 
379 static void Shutdown()
380 {
381  CloseHandle(hAken);
382  hAken = INVALID_HANDLE_VALUE;
383 
384  UninstallDriver();
385 }
386 
387 
389 
391 {
392  return ModuleInit(&initState, Init);
393 }
394 
396 {
397  ModuleShutdown(&initState, Shutdown);
398 }
USHORT port
Definition: aken.h:69
const size_t WVERSION_XP
Definition: wversion.h:33
u16 mahaf_ReadPort16(u16 port)
Definition: mahaf.cpp:68
#define u8
Definition: types.h:39
const Status LOGIC
Definition: status.h:409
static u32 ReadPort(u16 port, u8 numBytes)
Definition: mahaf.cpp:45
u32 mahaf_ReadPort32(u16 port)
Definition: mahaf.cpp:75
Status mahaf_Init()
Definition: mahaf.cpp:390
void mahaf_Shutdown()
Definition: mahaf.cpp:395
DWORD32 value
Definition: aken.h:62
DWORD64 reg
Definition: aken.h:97
const Status OK
Definition: status.h:386
some WinAPI functions SetLastError(0) on success, which is bad because it can hide previous errors...
Definition: wutil.h:119
static HANDLE hAken
Definition: mahaf.cpp:38
static Status Init()
Definition: h_mgr.cpp:744
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
#define IOCTL_AKEN_UNMAP
Definition: aken.h:43
static void Shutdown()
Definition: h_mgr.cpp:762
#define IOCTL_AKEN_READ_MSR
Definition: aken.h:44
unsigned short USHORT
Definition: wgl.h:52
const Status INVALID_HANDLE
Definition: status.h:419
int BOOL
Definition: wgl.h:51
int sprintf_s(char *buf, size_t max_chars, const char *fmt,...) PRINTF_ARGS(3)
static u64 ReadRegister(DWORD ioctl, u64 reg)
Definition: mahaf.cpp:156
#define ARRAY_SIZE(name)
UCHAR numBytes
Definition: aken.h:56
size_t wversion_Number()
Definition: wversion.cpp:65
const Status NOT_SUPPORTED
Definition: status.h:429
UCHAR numBytes
Definition: aken.h:70
void mahaf_WritePort8(u16 port, u8 value)
Definition: mahaf.cpp:95
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
intptr_t ModuleInitState
initialization state of a module (class, source file, etc.) must be initialized to zero (e...
Definition: module_init.h:35
DWORD64 virtualAddress
Definition: aken.h:85
void mahaf_WritePort32(u16 port, u32 value)
Definition: mahaf.cpp:105
Definition: path.h:75
void * HANDLE
Definition: wgl.h:62
void mahaf_UnmapPhysicalMemory(volatile void *virtualAddress)
Definition: mahaf.cpp:142
DWORD64 physicalAddress
Definition: aken.h:78
void mahaf_WriteModelSpecificRegister(u64 reg, u64 value)
Definition: mahaf.cpp:181
unsigned long DWORD
Definition: wgl.h:56
DWORD32 value
Definition: aken.h:68
u64 mahaf_ReadModelSpecificRegister(u64 reg)
Definition: mahaf.cpp:171
i64 Status
Error handling system.
Definition: status.h:171
USHORT port
Definition: aken.h:55
DWORD64 value
Definition: aken.h:103
T bits(T num, size_t lo_idx, size_t hi_idx)
extract the value of bits hi_idx:lo_idx within num
Definition: bits.h:97
volatile void * mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)
Definition: mahaf.cpp:122
#define PATH_MAX
Definition: wposix_types.h:101
void mahaf_WritePort16(u16 port, u16 value)
Definition: mahaf.cpp:100
DWORD64 numBytes
Definition: aken.h:79
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Definition: debug.h:331
#define IOCTL_AKEN_READ_PMC
Definition: aken.h:46
#define u16
Definition: types.h:40
static ModuleInitState initState
Definition: mahaf.cpp:388
#define u64
Definition: types.h:42
Status ModuleShutdown(volatile ModuleInitState *initState, void(*shutdown)())
calls a user-defined shutdown function if initState is &quot;initialized&quot;.
Definition: module_init.cpp:65
#define IOCTL_AKEN_MAP
Definition: aken.h:42
DWORD64 virtualAddress
Definition: aken.h:91
#define u32
Definition: types.h:41
bool mahaf_IsPhysicalMappingDangerous()
Definition: mahaf.cpp:111
const OsPath & wutil_ExecutablePath()
Definition: wutil.cpp:263
#define AKEN_NAME
Definition: aken.h:33
#define WARN_IF_FALSE(expression)
Definition: status.h:360
static void WritePort(u16 port, u32 value, u8 numBytes)
Definition: mahaf.cpp:82
#define IOCTL_AKEN_READ_PORT
Definition: aken.h:40
DWORD64 value
Definition: aken.h:110
u8 mahaf_ReadPort8(u16 port)
Definition: mahaf.cpp:61
bool wutil_HasCommandLineArgument(const wchar_t *arg)
Definition: wutil.cpp:236
static void UninstallDriver()
Definition: mahaf.cpp:220
Status ModuleInit(volatile ModuleInitState *initState, Status(*init)())
calls a user-defined init function if initState is zero.
Definition: module_init.cpp:40
#define IOCTL_AKEN_WRITE_PORT
Definition: aken.h:41
static std::string OsString(const OsPath &path)
Definition: os_path.h:42
bool wutil_IsWow64()
Definition: wutil.cpp:430
static SC_HANDLE OpenServiceControlManager(DWORD access)
Definition: mahaf.cpp:199
#define WARN_RETURN_0_IF_FALSE(expression)
Definition: status.h:369
u64 mahaf_ReadPerformanceMonitoringCounter(u64 reg)
Definition: mahaf.cpp:176
#define IOCTL_AKEN_WRITE_MSR
Definition: aken.h:45