Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
aken.c
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 // note: staticdv cannot yet check C++ code.
24 
25 #include <ntddk.h>
26 #include "aken.h"
27 #include "intrinsics.h"
28 
29 #define WIN32_NAME L"\\DosDevices\\Aken"
30 #define DEVICE_NAME L"\\Device\\Aken"
31 
32 // placate PREfast
33 DRIVER_INITIALIZE DriverEntry;
34 __drv_dispatchType(IRP_MJ_CREATE) DRIVER_DISPATCH AkenCreate;
35 __drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH AkenClose;
36 __drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH AkenDeviceControl;
37 DRIVER_UNLOAD AkenUnload;
38 
39 // this driver isn't large, but it's still slightly nicer to make its
40 // functions pageable and thus not waste precious non-paged pool.
41 // #pragma code_seg is more convenient than specifying alloc_text for
42 // every other function.
43 #pragma alloc_text(INIT, DriverEntry) // => discardable
44 #pragma code_seg(push, "PAGE")
45 
46 
47 //-----------------------------------------------------------------------------
48 // memory mapping
49 //-----------------------------------------------------------------------------
50 
51 /*
52 there are three approaches to mapping physical memory:
53 (http://www.microsoft.com/whdc/driver/kernel/mem-mgmt.mspx)
54 
55 - MmMapIoSpace (http://support.microsoft.com/kb/189327/en-us). despite the
56  name, it maps physical pages of any kind by allocating PTEs. very easy to
57  implement, but occupies precious kernel address space. possible bugs:
58  http://www.osronline.com/showThread.cfm?link=96737
59  http://support.microsoft.com/kb/925793/en-us
60 
61 - ZwMapViewOfSection of PhysicalMemory (http://tinyurl.com/yozmgy).
62  the code is a bit bulky, but the WinXP API prevents mapping pages with
63  conflicting attributes (see below).
64 
65 - MmMapLockedPagesSpecifyCache or MmGetSystemAddressForMdlSafe
66  (http://www.osronline.com/article.cfm?id=423). note: the latter is a macro
67  that calls the former. this is the 'normal' and fully documented way,
68  but it doesn't appear able to map a fixed physical address.
69  (MmAllocatePagesForMdl understandably doesn't work since some pages we
70  want to map are marked as unavailable for allocation, and I don't see
71  another documented way to fill an MDL with PFNs.)
72 
73 our choice here is forced by a very insidious issue. if someone else has
74 already mapped a page with different attributes (e.g. cacheable), TLBs
75 may end up corrupted, leading to disaster. the search for a documented
76 means of accessing the page frame database (to check if mapped anywhere
77 and determine the previously set attributes) has not borne fruit, so we
78 must use ZwMapViewOfSection. (if taking this up again, see
79 http://www.woodmann.com/forum/archive/index.php/t-6516.html )
80 
81 note that we guess if the page will have been mapped as cacheable and
82 even try the opposite if that turns out to have been incorrect.
83 */
84 
85 static int IsMemoryUncacheable(DWORD64 physicalAddress64)
86 {
87  PAGED_CODE();
88 
89  // original PC memory - contains BIOS
90  if(physicalAddress64 < 0x100000)
91  return 1;
92 
93  return 0;
94 }
95 
96 static NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWORD64 numBytes64, DWORD64* virtualAddress64)
97 {
98  NTSTATUS ntStatus;
99  HANDLE hMemory;
100  LARGE_INTEGER physicalAddress; // convenience
101  physicalAddress.QuadPart = physicalAddress64;
102 
103  PAGED_CODE();
104 
105  // get handle to PhysicalMemory object
106  {
107  OBJECT_ATTRIBUTES objectAttributes;
108  UNICODE_STRING objectName = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
109  const ULONG attributes = OBJ_CASE_INSENSITIVE;
110  const HANDLE rootDirectory = 0;
111  InitializeObjectAttributes(&objectAttributes, &objectName, attributes, rootDirectory, (PSECURITY_DESCRIPTOR)0);
112  ntStatus = ZwOpenSection(&hMemory, SECTION_ALL_ACCESS, &objectAttributes);
113  if(!NT_SUCCESS(ntStatus))
114  {
115  KdPrint(("AkenMapPhysicalMemory: ZwOpenSection failed\n"));
116  return ntStatus;
117  }
118  }
119 
120  // add a reference (required to prevent the handle from being deleted)
121  {
122  PVOID physicalMemorySection = NULL;
123  const POBJECT_TYPE objectType = 0; // allowed since specifying KernelMode
124  ntStatus = ObReferenceObjectByHandle(hMemory, SECTION_ALL_ACCESS, objectType, KernelMode, &physicalMemorySection, 0);
125  if(!NT_SUCCESS(ntStatus))
126  {
127  KdPrint(("AkenMapPhysicalMemory: ObReferenceObjectByHandle failed\n"));
128  goto close_handle;
129  }
130  }
131 
132  // note: mapmem.c does HalTranslateBusAddress, but we only care about
133  // system memory. translating doesn't appear to be necessary, even if
134  // much existing code uses it (probably due to cargo cult).
135 
136  // map desired memory into user PTEs
137  {
138  const HANDLE hProcess = (HANDLE)-1;
139  PVOID virtualBaseAddress = 0; // let ZwMapViewOfSection pick
140  const ULONG zeroBits = 0; // # high-order bits in address that must be 0
141  SIZE_T mappedSize = (SIZE_T)numBytes64; // will receive the actual page-aligned size
142  LARGE_INTEGER physicalBaseAddress = physicalAddress; // will be rounded down to 64KB boundary
143  const SECTION_INHERIT inheritDisposition = ViewShare;
144  const ULONG allocationType = 0;
145  ULONG protect = PAGE_READWRITE;
146  if(IsMemoryUncacheable(physicalAddress64))
147  protect |= PAGE_NOCACHE;
148  ntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect);
149  if(!NT_SUCCESS(ntStatus))
150  {
151  // try again with the opposite cacheability attribute
152  protect ^= PAGE_NOCACHE;
153  ntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect);
154  if(!NT_SUCCESS(ntStatus))
155  {
156  KdPrint(("AkenMapPhysicalMemory: ZwMapViewOfSection failed\n"));
157  goto close_handle;
158  }
159  }
160 
161  // the mapping rounded our physical base address down to the nearest
162  // 64KiB boundary, so adjust the virtual address accordingly.
163  {
164  const DWORD32 numBytesRoundedDown = physicalAddress.LowPart - physicalBaseAddress.LowPart;
165  ASSERT(numBytesRoundedDown < 0x10000);
166  *virtualAddress64 = (DWORD64)virtualBaseAddress + numBytesRoundedDown;
167  }
168  }
169 
170  ntStatus = STATUS_SUCCESS;
171 
172 close_handle:
173  // closing the handle even on success means that callers won't have to
174  // pass it back when unmapping. why does this work? ZwMapViewOfSection
175  // apparently adds a reference to hMemory.
176  ZwClose(hMemory);
177 
178  return ntStatus;
179 }
180 
181 
182 static NTSTATUS AkenUnmapPhysicalMemory(const DWORD64 virtualAddress)
183 {
184  PAGED_CODE();
185 
186  {
187  const HANDLE hProcess = (HANDLE)-1;
188  PVOID baseAddress = (PVOID)virtualAddress;
189  NTSTATUS ntStatus = ZwUnmapViewOfSection(hProcess, baseAddress);
190  if(!NT_SUCCESS(ntStatus))
191  {
192  KdPrint(("AkenUnmapPhysicalMemory: ZwUnmapViewOfSection failed\n"));
193  return ntStatus;
194  }
195  }
196 
197  return STATUS_SUCCESS;
198 }
199 
200 
201 //-----------------------------------------------------------------------------
202 // helper functions called from DeviceControl
203 //-----------------------------------------------------------------------------
204 
205 static NTSTATUS AkenIoctlReadPort(PVOID buf, const ULONG inSize, ULONG* outSize)
206 {
207  DWORD32 value;
208 
209  PAGED_CODE();
210 
211  if(inSize != sizeof(AkenReadPortIn) || *outSize != sizeof(AkenReadPortOut))
212  return STATUS_BUFFER_TOO_SMALL;
213 
214  {
215  const AkenReadPortIn* in = (const AkenReadPortIn*)buf;
216  const USHORT port = in->port;
217  const UCHAR numBytes = in->numBytes;
218  switch(numBytes)
219  {
220  case 1:
221  value = (DWORD32)READ_PORT_UCHAR((PUCHAR)port);
222  break;
223  case 2:
224  value = (DWORD32)READ_PORT_USHORT((PUSHORT)port);
225  break;
226  case 4:
227  value = (DWORD32)READ_PORT_ULONG((PULONG)port);
228  break;
229  default:
230  return STATUS_INVALID_PARAMETER;
231  }
232  }
233 
234  {
236  out->value = value;
237  }
238  return STATUS_SUCCESS;
239 }
240 
241 static NTSTATUS AkenIoctlWritePort(PVOID buf, const ULONG inSize, ULONG* outSize)
242 {
243  PAGED_CODE();
244 
245  if(inSize != sizeof(AkenWritePortIn) || *outSize != 0)
246  return STATUS_BUFFER_TOO_SMALL;
247 
248  {
249  const AkenWritePortIn* in = (const AkenWritePortIn*)buf;
250  const DWORD32 value = in->value;
251  const USHORT port = in->port;
252  const UCHAR numBytes = in->numBytes;
253  switch(numBytes)
254  {
255  case 1:
256  WRITE_PORT_UCHAR((PUCHAR)port, (UCHAR)(value & 0xFF));
257  break;
258  case 2:
259  WRITE_PORT_USHORT((PUSHORT)port, (USHORT)(value & 0xFFFF));
260  break;
261  case 4:
262  WRITE_PORT_ULONG((PULONG)port, value);
263  break;
264  default:
265  return STATUS_INVALID_PARAMETER;
266  }
267  }
268 
269  return STATUS_SUCCESS;
270 }
271 
272 
273 static NTSTATUS AkenIoctlMap(PVOID buf, const ULONG inSize, ULONG* outSize)
274 {
275  DWORD64 virtualAddress;
276  NTSTATUS ntStatus;
277 
278  PAGED_CODE();
279 
280  if(inSize != sizeof(AkenMapIn) || *outSize != sizeof(AkenMapOut))
281  return STATUS_BUFFER_TOO_SMALL;
282 
283  {
284  const AkenMapIn* in = (const AkenMapIn*)buf;
285  const DWORD64 physicalAddress = in->physicalAddress;
286  const DWORD64 numBytes = in->numBytes;
287  ntStatus = AkenMapPhysicalMemory(physicalAddress, numBytes, &virtualAddress);
288  }
289 
290  {
291  AkenMapOut* out = (AkenMapOut*)buf;
292  out->virtualAddress = virtualAddress;
293  }
294  return ntStatus;
295 }
296 
297 static NTSTATUS AkenIoctlUnmap(PVOID buf, const ULONG inSize, ULONG* outSize)
298 {
299  NTSTATUS ntStatus;
300 
301  PAGED_CODE();
302 
303  if(inSize != sizeof(AkenUnmapIn) || *outSize != 0)
304  return STATUS_BUFFER_TOO_SMALL;
305 
306  {
307  const AkenUnmapIn* in = (const AkenUnmapIn*)buf;
308  const DWORD64 virtualAddress = in->virtualAddress;
309  ntStatus = AkenUnmapPhysicalMemory(virtualAddress);
310  }
311 
312  return ntStatus;
313 }
314 
315 
316 static NTSTATUS AkenIoctlReadModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG* outSize)
317 {
318  DWORD64 value;
319 
320  PAGED_CODE();
321 
322  if(inSize != sizeof(AkenReadRegisterIn) || *outSize != sizeof(AkenReadRegisterOut))
323  return STATUS_BUFFER_TOO_SMALL;
324 
325  {
326  const AkenReadRegisterIn* in = (const AkenReadRegisterIn*)buf;
327  const DWORD64 reg = in->reg;
328  value = __readmsr((int)reg);
329  }
330 
331  {
333  out->value = value;
334  }
335 
336  return STATUS_SUCCESS;
337 }
338 
339 static NTSTATUS AkenIoctlWriteModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG* outSize)
340 {
341  PAGED_CODE();
342 
343  if(inSize != sizeof(AkenWriteRegisterIn) || *outSize != 0)
344  return STATUS_BUFFER_TOO_SMALL;
345 
346  {
347  const AkenWriteRegisterIn* in = (const AkenWriteRegisterIn*)buf;
348  const DWORD64 reg = in->reg;
349  const DWORD64 value = in->value;
350  __writemsr((unsigned long)reg, value);
351  }
352 
353  return STATUS_SUCCESS;
354 }
355 
356 static NTSTATUS AkenIoctlReadPerformanceMonitoringCounter(PVOID buf, const ULONG inSize, ULONG* outSize)
357 {
358  DWORD64 value;
359 
360  PAGED_CODE();
361 
362  if(inSize != sizeof(AkenReadRegisterIn) || *outSize != sizeof(AkenReadRegisterOut))
363  return STATUS_BUFFER_TOO_SMALL;
364 
365  {
366  const AkenReadRegisterIn* in = (const AkenReadRegisterIn*)buf;
367  const DWORD64 reg = in->reg;
368  value = __readpmc((unsigned long)reg);
369  }
370 
371  {
373  out->value = value;
374  }
375 
376  return STATUS_SUCCESS;
377 }
378 
379 
380 static NTSTATUS AkenIoctlUnknown(PVOID buf, const ULONG inSize, ULONG* outSize)
381 {
382  PAGED_CODE();
383 
384  KdPrint(("AkenIoctlUnknown\n"));
385 
386  *outSize = 0;
387  return STATUS_INVALID_DEVICE_REQUEST;
388 }
389 
390 
391 typedef NTSTATUS (*AkenIoctl)(PVOID buf, ULONG inSize, ULONG* outSize);
392 
393 static AkenIoctl AkenIoctlFromCode(ULONG ioctlCode)
394 {
395  PAGED_CODE();
396 
397  switch(ioctlCode)
398  {
400  return AkenIoctlReadPort;
402  return AkenIoctlWritePort;
403 
404  case IOCTL_AKEN_MAP:
405  return AkenIoctlMap;
406  case IOCTL_AKEN_UNMAP:
407  return AkenIoctlUnmap;
408 
409  case IOCTL_AKEN_READ_MSR:
413 
414  default:
415  return AkenIoctlUnknown;
416  }
417 }
418 
419 
420 //-----------------------------------------------------------------------------
421 // entry points
422 //-----------------------------------------------------------------------------
423 
424 static NTSTATUS AkenCreate(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
425 {
426  PAGED_CODE();
427 
428  irp->IoStatus.Status = STATUS_SUCCESS;
429  irp->IoStatus.Information = 0;
430  IoCompleteRequest(irp, IO_NO_INCREMENT);
431  return STATUS_SUCCESS;
432 }
433 
434 
435 static NTSTATUS AkenClose(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
436 {
437  PAGED_CODE();
438 
439  // same as AkenCreate ATM
440  irp->IoStatus.Status = STATUS_SUCCESS;
441  irp->IoStatus.Information = 0;
442  IoCompleteRequest(irp, IO_NO_INCREMENT);
443  return STATUS_SUCCESS;
444 }
445 
446 
447 static NTSTATUS AkenDeviceControl(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
448 {
449  PAGED_CODE();
450 
451  {
452  // get buffer from IRP. all our IOCTLs are METHOD_BUFFERED, so buf is
453  // allocated by the I/O manager and used for both input and output.
454  PVOID buf = irp->AssociatedIrp.SystemBuffer;
455  PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(irp);
456  ULONG ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
457  const ULONG inSize = irpStack->Parameters.DeviceIoControl.InputBufferLength;
458  ULONG outSize = irpStack->Parameters.DeviceIoControl.OutputBufferLength; // modified by AkenIoctl*
459 
460  const AkenIoctl akenIoctl = AkenIoctlFromCode(ioctlCode);
461  const NTSTATUS ntStatus = akenIoctl(buf, inSize, &outSize);
462 
463  irp->IoStatus.Information = outSize; // number of bytes to copy from buf to user's buffer
464  irp->IoStatus.Status = ntStatus;
465  IoCompleteRequest(irp, IO_NO_INCREMENT);
466  return ntStatus;
467  }
468 }
469 
470 
471 static VOID AkenUnload(IN PDRIVER_OBJECT driverObject)
472 {
473  PAGED_CODE();
474 
475  KdPrint(("AkenUnload\n"));
476 
477  {
478  UNICODE_STRING win32Name = RTL_CONSTANT_STRING(WIN32_NAME);
479  IoDeleteSymbolicLink(&win32Name);
480  }
481 
482  if(driverObject->DeviceObject)
483  IoDeleteDevice(driverObject->DeviceObject);
484 }
485 
486 
487 #pragma code_seg(pop) // make sure we don't countermand the alloc_text
488 
489 NTSTATUS DriverEntry(IN PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath)
490 {
491  UNICODE_STRING deviceName = RTL_CONSTANT_STRING(DEVICE_NAME);
492 
493  // create device object
494  PDEVICE_OBJECT deviceObject;
495  {
496  const ULONG deviceExtensionSize = 0;
497  const ULONG deviceCharacteristics = FILE_DEVICE_SECURE_OPEN;
498  const BOOLEAN exlusive = TRUE;
499  NTSTATUS ntStatus = IoCreateDevice(driverObject, deviceExtensionSize, &deviceName, FILE_DEVICE_AKEN, deviceCharacteristics, exlusive, &deviceObject);
500  if(!NT_SUCCESS(ntStatus))
501  {
502  KdPrint(("DriverEntry: IoCreateDevice failed\n"));
503  return ntStatus;
504  }
505  }
506 
507  // set entry points
508  driverObject->MajorFunction[IRP_MJ_CREATE] = AkenCreate;
509  driverObject->MajorFunction[IRP_MJ_CLOSE] = AkenClose;
510  driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = AkenDeviceControl;
511  driverObject->DriverUnload = AkenUnload;
512 
513  // symlink NT device name to Win32 namespace
514  {
515  UNICODE_STRING win32Name = RTL_CONSTANT_STRING(WIN32_NAME);
516  NTSTATUS ntStatus = IoCreateSymbolicLink(&win32Name, &deviceName);
517  if(!NT_SUCCESS(ntStatus))
518  {
519  KdPrint(("DriverEntry: IoCreateSymbolicLink failed\n"));
520  IoDeleteDevice(deviceObject);
521  return ntStatus;
522  }
523  }
524 
525  return STATUS_SUCCESS;
526 }
USHORT port
Definition: aken.h:69
DRIVER_INITIALIZE DriverEntry
Definition: aken.c:33
DWORD32 value
Definition: aken.h:62
DWORD64 reg
Definition: aken.h:97
static NTSTATUS AkenUnmapPhysicalMemory(const DWORD64 virtualAddress)
Definition: aken.c:182
static NTSTATUS AkenIoctlUnknown(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:380
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
static NTSTATUS AkenIoctlReadPerformanceMonitoringCounter(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:356
#define DEVICE_NAME
Definition: aken.c:30
#define IOCTL_AKEN_UNMAP
Definition: aken.h:43
#define IOCTL_AKEN_READ_MSR
Definition: aken.h:44
static NTSTATUS AkenIoctlReadPort(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:205
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
Definition: debug.h:310
unsigned short USHORT
Definition: wgl.h:52
NTSTATUS(* AkenIoctl)(PVOID buf, ULONG inSize, ULONG *outSize)
Definition: aken.c:391
static NTSTATUS AkenIoctlMap(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:273
static HANDLE hProcess
Definition: wdbg_sym.cpp:60
UCHAR numBytes
Definition: aken.h:56
#define FILE_DEVICE_AKEN
Definition: aken.h:36
UCHAR numBytes
Definition: aken.h:70
static NTSTATUS AkenIoctlWritePort(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:241
DWORD64 virtualAddress
Definition: aken.h:85
void * HANDLE
Definition: wgl.h:62
DWORD64 physicalAddress
Definition: aken.h:78
static NTSTATUS AkenIoctlUnmap(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:297
static NTSTATUS AkenClose(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
Definition: aken.c:435
DWORD32 value
Definition: aken.h:68
__drv_dispatchType(IRP_MJ_CREATE)
Definition: aken.c:34
void VOID
Definition: wgl.h:49
USHORT port
Definition: aken.h:55
DWORD64 value
Definition: aken.h:103
static NTSTATUS AkenIoctlWriteModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:339
DWORD64 numBytes
Definition: aken.h:79
static NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWORD64 numBytes64, DWORD64 *virtualAddress64)
Definition: aken.c:96
static NTSTATUS AkenIoctlReadModelSpecificRegister(PVOID buf, const ULONG inSize, ULONG *outSize)
Definition: aken.c:316
#define IOCTL_AKEN_MAP
Definition: aken.h:42
DWORD64 virtualAddress
Definition: aken.h:91
#define WIN32_NAME
Definition: aken.c:29
static AkenIoctl AkenIoctlFromCode(ULONG ioctlCode)
Definition: aken.c:393
static VOID AkenUnload(IN PDRIVER_OBJECT driverObject)
Definition: aken.c:471
#define IOCTL_AKEN_READ_PORT
Definition: aken.h:40
static NTSTATUS AkenCreate(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
Definition: aken.c:424
DWORD64 value
Definition: aken.h:110
static NTSTATUS AkenDeviceControl(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
Definition: aken.c:447
#define IOCTL_AKEN_WRITE_PORT
Definition: aken.h:41
#define IOCTL_AKEN_WRITE_MSR
Definition: aken.h:45