// Gerätetreiber zum direkten Zugriff auf Systemressourcen
// unter Windows NT 4.0, c't 1/97 Matthias Witthopf, Andreas Stiller

extern "C"
{
#include <ntddk.h>
#include <string.h>
#include <stdlib.h>
#include <devioctl.h>
#include <conio.h>
#include "directnt.h"
}

#define DIRECTNT_DEVICE_NAME L"\\Device\\DirectNT"
#define DOS_DEVICE_NAME      L"\\DosDevices\\Dev_DirectNT"
#define IOPM_SIZE 0x2000

typedef UCHAR IOPMTYP[IOPM_SIZE];
typedef struct {
  ULONG Dummy;
  IOPMTYP iopm;
} TLocalDevInfo,* PLocalDevInfo;

extern "C" void Ke386SetIoAccessMap     (int,IOPMTYP);
extern "C" void Ke386QueryIoAccessMap   (int,IOPMTYP);
extern "C" void Ke386IoSetAccessProcess (PEPROCESS, int);

__declspec(dllimport)  BOOLEAN MmIsAddressValid  (IN PVOID );
__declspec(dllimport)  PVOID   MmMapIoSpace  (PHYSICAL_ADDRESS,ULONG,BOOLEAN);
__declspec(dllimport)  VOID    MmUnmapIoSpace (PVOID,ULONG);

NTSTATUS DirectNT_CreateDevice(IN  PWSTR PrototypeName, 
    IN  DEVICE_TYPE DeviceType, IN  PDRIVER_OBJECT DriverObject,
    OUT PDEVICE_OBJECT *ppDevObj)
{
  UNICODE_STRING NtDeviceName;
  UNICODE_STRING Win32DeviceName;
  RtlInitUnicodeString(&NtDeviceName,PrototypeName);
  NTSTATUS Status = IoCreateDevice(DriverObject,sizeof(TLocalDevInfo),
                            &NtDeviceName,DeviceType,0,FALSE,ppDevObj);
  if (!NT_SUCCESS(Status)) return Status;
  RtlZeroMemory((*ppDevObj)->DeviceExtension,sizeof(TLocalDevInfo));
  RtlInitUnicodeString(&Win32DeviceName,DOS_DEVICE_NAME);
  Status = IoCreateSymbolicLink(&Win32DeviceName,&NtDeviceName);
  if (!NT_SUCCESS(Status))
    IoDeleteDevice(*ppDevObj);
  return Status;
}

void SetIOPermissionFree(int OnFlag,int pstart,int pend,IOPMTYP iopm)
{
  Ke386QueryIoAccessMap(1,iopm);
  for (int j=pstart; j<=pend; j++)
    iopm[j/8] &= ~(1 << (j%8));
  Ke386IoSetAccessProcess(PsGetCurrentProcess(), OnFlag);
  Ke386SetIoAccessMap (1,iopm);
}

void SetIOPermissionLock(int OnFlag,int pstart,int pend,IOPMTYP iopm)
{
  Ke386QueryIoAccessMap(1,iopm);
  for (int j=pstart; j<=pend; j++)
    iopm[j/8] |= (1 << (j%8));
  Ke386IoSetAccessProcess(PsGetCurrentProcess(), OnFlag);
  Ke386SetIoAccessMap (1,iopm);
}

NTSTATUS DirectNT_Control(IN PLocalDevInfo pLDI, IN PIRP pIrp,
   IN PIO_STACK_LOCATION IrpStack, IN ULONG IoctlCode)
{
  PDirectNTInfo InBuf=(PDirectNTInfo)pIrp->AssociatedIrp.SystemBuffer;
  ULONG InBufSize=IrpStack->Parameters.DeviceIoControl.InputBufferLength;
  ULONG OpCode=InBuf->OpCode;
  void *OutBuf=(void *)pIrp->AssociatedIrp.SystemBuffer;
  ULONG OutBufSize=IrpStack->Parameters.DeviceIoControl.OutputBufferLength;

  switch(OpCode) {

  case OP_Check:
    *(PULONG)OutBuf = 0x12345678;
    pIrp->IoStatus.Information = sizeof(ULONG);
    return STATUS_SUCCESS;

  case OP_GiveIO:
    SetIOPermissionFree(1,InBuf->Par1,InBuf->Par2,pLDI->iopm);
    pIrp->IoStatus.Information = 0;
    return STATUS_SUCCESS;

  case OP_LoadIOPM:
    Ke386QueryIoAccessMap(1,(UCHAR*)OutBuf);
    pIrp->IoStatus.Information = IOPM_SIZE;
    return STATUS_SUCCESS;

  case OP_LockIO:
    SetIOPermissionLock(1,InBuf->Par1,InBuf->Par2,pLDI->iopm);
    pIrp->IoStatus.Information = 0;
    return STATUS_SUCCESS;

  case OP_GetCR0:
    {
      ULONG Result;
      __asm MOV EAX,CR0
      __asm MOV Result,EAX
      *(PULONG)OutBuf = Result;
      pIrp->IoStatus.Information = sizeof(ULONG);
      return STATUS_SUCCESS;
    }

  case OP_SetCR0:
    {
      ULONG Value = InBuf->Par1;
      __asm 
       { MOV EAX,Value
         __emit 0x0F 				  // = wbinvd, Achtung ist in Visual C++ 2.0 
	     __emit 0x09				  //   vorhanden, aber falsch impementiert!!!
         MOV CR0,EAX
       }
      pIrp->IoStatus.Information = 0;
      return STATUS_SUCCESS;
    }

  case OP_GetMR:
    {
      ULONG Resultlo;
      ULONG Resulthi;
      ULONG Index = InBuf->Par1;
      __asm {
        Push ecx
        push edx
        MOV ECX,Index
        __emit 0x0F             //RDMSR
        __emit 0x32
        MOV Resultlo,Eax
        MOV Resulthi,Edx
        Pop  ecx
        pop  edx
      }
      *(PULONG)OutBuf = Resultlo;
      *((PULONG)(OutBuf)+1) = Resulthi;
      pIrp->IoStatus.Information = 2*sizeof(ULONG);
      return STATUS_SUCCESS;
    }

  case OP_SetMR:
    {
      ULONG Index    = InBuf->Par1;
      ULONG Valuelo  = InBuf->Par2;
      ULONG Valuehi  = InBuf->Par3;
      __asm  {
        push ecx
        push edx
        MOV ECX,Index
        MOV EAX,Valuelo
        MOV EDX,Valuehi
        __emit 0x0F      // WRMSR
        __emit 0x30
        pop  edx
        pop  ecx
      }
      pIrp->IoStatus.Information = 0;
      return STATUS_SUCCESS;
    }

  case OP_ReadPortByte:
    *(PUCHAR)OutBuf = READ_PORT_UCHAR((PUCHAR)InBuf->Par1);
    pIrp->IoStatus.Information = sizeof(UCHAR);
    return STATUS_SUCCESS;

  case OP_WritePortByte:
    WRITE_PORT_UCHAR((PUCHAR)InBuf->Par1,(UCHAR)InBuf->Par2);
    pIrp->IoStatus.Information = 0;
    return STATUS_SUCCESS;
  
  
  case OP_WritePCIDword:  // Konfigmechanismus 1 
	 { 
		  ULONG cfb=_inp(0xCFB); 
		  ULONG confadd= (1 << 31) | (ULONG)InBuf->Par1 | (ULONG) InBuf->Par2 << 10; 
		  _asm  pushf
		  _asm	cli
		  _outp  (0xCFB, cfb | 1);
		  _outp  (0xCFA ,0);
		  _outpd (0xCF8,confadd);
		  _outpd (0xCFC,(ULONG)InBuf->Par3);
		  _outp  (0xCFB,cfb);
		  _asm  popf 
          pIrp->IoStatus.Information = 0;
          return STATUS_SUCCESS;
     }     

   case OP_ReadPCIDword: //Konfigmechanismus 1 
	  { 
		  ULONG cfb=_inp(0xCFB); 
		  ULONG confadd= (1 << 31) | (ULONG)InBuf->Par1 | (ULONG) InBuf->Par2 <<10 ; 
		  _asm  pushf;
		  _asm	cli;
		  _outp  (0xCFB, cfb | 1);
		  _outp  (0xCFA ,0);
		  _outpd (0xCF8,confadd);
		  *(PULONG)OutBuf = _inpd(0xCFC);
 		  _outp  (0xCFB,cfb);
		  _asm  popf 
		  pIrp->IoStatus.Information = sizeof(ULONG);
          return STATUS_SUCCESS;
	   }     
	   			
  case OP_ReadFlags:
	  {
		 ULONG EFlags;
	   	_asm 	
	   	  {
	   	  pushfd
		  pop eax
		  mov EFlags,eax
		  }
		  pIrp->IoStatus.Information = sizeof(ULONG);
          return STATUS_SUCCESS;
	   }
	 
/*  case OP_ReadMemDword:
	
	if (MmIsAddressValid)  
     {
     *(PULONG)OutBuf = *(ULONG*) (InBuf->Par1);
     pIrp->IoStatus.Information = sizeof(ULONG);
     return STATUS_SUCCESS;
	 }
	else 
	 {
	 pIrp->IoStatus.Information = 0;
     return STATUS_ACCESS_VIOLATION;
  	 }
*/	
   case OP_ReadPhysMemDword:
	PHYSICAL_ADDRESS PADDR;
	PADDR.LowPart=(ULONG) (InBuf->Par1);
	PADDR.HighPart=0;					// Reserve für > 4GByte 
	PVOID Linadr=MmMapIoSpace (PADDR,4,MmNonCached);
 	*(PULONG)OutBuf = *(ULONG*) Linadr; 
	MmUnmapIoSpace (Linadr,4); 
    pIrp->IoStatus.Information = sizeof(ULONG);
    return STATUS_SUCCESS;


  }
  return STATUS_INVALID_PARAMETER;
}



NTSTATUS DirectNT_Dispatch(IN PDEVICE_OBJECT pDO,IN PIRP pIrp)
{
  pIrp->IoStatus.Information = 0;  // Anzahl Rückgabe-Bytes
  PLocalDevInfo      pLDI      = (PLocalDevInfo)pDO->DeviceExtension;
  PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  NTSTATUS           Status    = STATUS_NOT_IMPLEMENTED;

  switch(pIrpStack->MajorFunction) {
  case IRP_MJ_CREATE:
  case IRP_MJ_CLOSE:
    Status = STATUS_SUCCESS;
    break;
  case IRP_MJ_DEVICE_CONTROL:
    switch(pIrpStack->Parameters.DeviceIoControl.IoControlCode) {
      case IOCTL_DIRECTNT_CONTROL:
        Status = DirectNT_Control(pLDI,pIrp,pIrpStack,
          pIrpStack->Parameters.DeviceIoControl.IoControlCode);
        break;
      }
    break;
  }
  pIrp->IoStatus.Status = Status;
  IoCompleteRequest(pIrp,IO_NO_INCREMENT);
  return Status;
}

VOID DirectNT_Unload(PDRIVER_OBJECT DriverObject)
{ // Symbolischen Link wieder auflösen
  UNICODE_STRING Win32DeviceName;
  RtlInitUnicodeString(&Win32DeviceName,DOS_DEVICE_NAME);
  IoDeleteSymbolicLink(&Win32DeviceName);
  IoDeleteDevice(DriverObject->DeviceObject);
}

extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
                                IN PUNICODE_STRING RegistryPath)
{ // Symbolischen Link erzeugen, so daß Win32 darauf zugreifen kann
  DriverObject->MajorFunction[IRP_MJ_CREATE]         = DirectNT_Dispatch;
  DriverObject->MajorFunction[IRP_MJ_CLOSE]          = DirectNT_Dispatch;
  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DirectNT_Dispatch;
  DriverObject->DriverUnload                         = DirectNT_Unload;
  PDEVICE_OBJECT DeviceObject;
  return DirectNT_CreateDevice(DIRECTNT_DEVICE_NAME,DIRECTNT_TYPE,
                               DriverObject,&DeviceObject);
}

