0ad/source/lib/sysdep/win/aken/aken.cpp
janwas cded0936c5 # housekeeping
. update moduleInit mechanism and move it out of lib.cpp. now has a
thread-safe reference count and allows repeated init/shutdown (required
for self-tests)
. vfs now depends on h_mgr; h_mgr need therefore no longer explicitly be
shutdown (e.g. in self-tests)
. acpi: minor fixes
. cpu: AtomicAdd and CAS now take volatile variable (safer)
. mahaf: unload driver on shutdown (did this for DriverVerifier, but
it's 'cleaner' in general)
. wdbg_sym
. wfilesystem: get rid of manual declarations of deprecated functions;
implement them via trampoline instead

This was SVN commit r5086.
2007-05-21 23:24:56 +00:00

326 lines
9.9 KiB
C++

extern "C" { // must come before ntddk.h
#include <ntddk.h>
#include "aken.h"
#define KDPRINT KdPrint(("AKEN: ")); KdPrint
#define WIN32_NAME L"\\DosDevices\\Aken"
#define DEVICE_NAME L"\\Device\\Aken"
// note: this driver isn't much larger than a page anyway, so
// there's little point in using #pragma alloc_text.
//-----------------------------------------------------------------------------
// memory mapping
//-----------------------------------------------------------------------------
// references: DDK mapmem.c sample,
// http://support.microsoft.com/kb/189327/en-us
static NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWORD64 numBytes64, DWORD64& virtualAddress64)
{
NTSTATUS ntStatus;
// (convenience)
LARGE_INTEGER physicalAddress;
physicalAddress.QuadPart = physicalAddress64;
// get handle to PhysicalMemory object
HANDLE hMemory;
{
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(("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(("ObReferenceObjectByHandle failed\n"));
goto close_handle;
}
}
// map desired memory into user PTEs (note: don't use MmMapIoSpace
// because that occupies precious non-paged pool)
{
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;
const ULONG protect = PAGE_READWRITE|PAGE_NOCACHE;
ntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect);
if(!NT_SUCCESS(ntStatus))
{
KDPRINT(("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)
{
const HANDLE hProcess = (HANDLE)-1;
PVOID baseAddress = (PVOID)virtualAddress;
NTSTATUS ntStatus = ZwUnmapViewOfSection(hProcess, baseAddress);
if(!NT_SUCCESS(ntStatus))
{
KDPRINT(("ZwUnmapViewOfSection failed\n"));
return ntStatus;
}
return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------
// helper functions called from DeviceControl
//-----------------------------------------------------------------------------
static NTSTATUS AkenIoctlReadPort(PVOID buf, const ULONG inSize, ULONG& outSize)
{
KDPRINT(("IOCTL_AKEN_READ_PORT\n"));
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;
DWORD32 value;
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;
}
KDPRINT((" port %x = %x\n", port, value));
AkenReadPortOut* out = (AkenReadPortOut*)buf;
out->value = value;
return STATUS_SUCCESS;
}
static NTSTATUS AkenIoctlWritePort(PVOID buf, const ULONG inSize, ULONG& outSize)
{
KDPRINT(("IOCTL_AKEN_WRITE_PORT\n"));
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;
}
KDPRINT((" port %x := %x\n", port, value));
return STATUS_SUCCESS;
}
static NTSTATUS AkenIoctlMap(PVOID buf, const ULONG inSize, ULONG& outSize)
{
KDPRINT(("IOCTL_AKEN_MAP\n"));
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;
DWORD64 virtualAddress;
NTSTATUS ntStatus = AkenMapPhysicalMemory(physicalAddress, numBytes, virtualAddress);
AkenMapOut* out = (AkenMapOut*)buf;
out->virtualAddress = virtualAddress;
return ntStatus;
}
static NTSTATUS AkenIoctlUnmap(PVOID buf, const ULONG inSize, ULONG& outSize)
{
KDPRINT(("IOCTL_AKEN_UNMAP\n"));
if(inSize != sizeof(AkenUnmapIn) || outSize != 0)
return STATUS_BUFFER_TOO_SMALL;
const AkenUnmapIn* in = (const AkenUnmapIn*)buf;
const DWORD64 virtualAddress = in->virtualAddress;
NTSTATUS ntStatus = AkenUnmapPhysicalMemory(virtualAddress);
return ntStatus;
}
static NTSTATUS AkenIoctlUnknown(PVOID buf, const ULONG inSize, ULONG& outSize)
{
KDPRINT(("Unknown IOCTL\n"));
outSize = 0;
return STATUS_INVALID_DEVICE_REQUEST;
}
typedef NTSTATUS (*AkenIoctl)(PVOID buf, ULONG inSize, ULONG& outSize);
static inline AkenIoctl AkenIoctlFromCode(ULONG ioctlCode)
{
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;
default:
return AkenIoctlUnknown;
}
}
//-----------------------------------------------------------------------------
// entry points
//-----------------------------------------------------------------------------
// used as Create and Close entry point since this driver is stateless.
static NTSTATUS MarkRequestComplete(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
{
irp->IoStatus.Status = STATUS_SUCCESS;
irp->IoStatus.Information = 0;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
static NTSTATUS DeviceControl(IN PDEVICE_OBJECT deviceObject, IN PIRP irp)
{
// 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 Unload(IN PDRIVER_OBJECT driverObject)
{
KDPRINT(("Unload\n"));
UNICODE_STRING win32Name = RTL_CONSTANT_STRING(WIN32_NAME);
IoDeleteSymbolicLink(&win32Name);
if(driverObject->DeviceObject)
IoDeleteDevice(driverObject->DeviceObject);
}
NTSTATUS DriverEntry(IN OUT PDRIVER_OBJECT driverObject, IN PUNICODE_STRING registryPath)
{
KDPRINT(("DriverEntry\n"));
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(("IoCreateDevice failed\n"));
return ntStatus;
}
}
// set entry points
driverObject->MajorFunction[IRP_MJ_CREATE] = MarkRequestComplete;
driverObject->MajorFunction[IRP_MJ_CLOSE] = MarkRequestComplete;
driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceControl;
driverObject->DriverUnload = Unload;
// 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(("IoCreateSymbolicLink failed\n"));
IoDeleteDevice(deviceObject);
return ntStatus;
}
}
return STATUS_SUCCESS;
}
} // extern "C" {