/*********************************************************************
Author: Dale Roberts - Henrik Haftmann
Date: 8/30/95 - 150420 - 170113
Program: GIVEIO.SYS
Compile: Use DDK BUILD facility
Purpose: Give direct port I/O access to a user mode process. Win64
*********************************************************************/
#include <ntddk.h>
// Platz für die TSS-Kopien, dazu eine IOPM (non-pageable)
// Das spart Platz und stattet alle Prozessoren mit gleicher IOPM aus.
// Dazu wird der Zeiger auf die IOPM entsprechend umgesetzt.
// [en:IOPMs are placed in a way that all processors share one IOPM]
typedef struct{
UCHAR data[102]; // Ring-Stacks, Interrupt-Stacks
USHORT iopm_offset;
}TSS; // 104 (0x68) Byte
typedef UCHAR IOPM[0x2000]; // 8 KByte
typedef struct{
TSS tss[MAXIMUM_PROCESSORS]; // 104*64 = 6656 (0x1A00) Byte
IOPM iopm;
}TSSLIST; // knapp 15 KByte (= TSS-Limit für Prozessor 0)
TSSLIST tsslist; // .bss == NonPagedPool
//amd64.asm exports:
// void Ke386SetIoAccessMap(int, IOPM*);
// void Ke386QueryIoAccessMap(int, IOPM*);
// void Ke386IoSetAccessProcess(PEPROCESS, int);
void EachProcessorDpc(KDPC*,PVOID,PVOID,PVOID);
const TSS*GetTSS();
void SetTSS(USHORT /*cx*/,const TSS* /*rdx*/);
void RevertTSS(void*);
void _movsq(ULONG,void*,const void*);
// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
void SetIOPermissionMap(void*dummy) {
TSSLIST*l=&tsslist;
TSS*p=l->tss+KeGetCurrentProcessorNumber();
// USHORT o=(USHORT)(l->iopm-p->data); // Offset zur IOPM (passt in 16 Bit)
_movsq(sizeof(TSS)/8,p,GetTSS()); // Gesamte TSS kopieren (13 QWords)
// p->iopm_offset=o; // Eintrag ändern
SetTSS(0x68/*o+0x2000*/,p); // GDT ändern, TR laden, GDT zurück
}
// IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
// Ruft die angegebene, in Assembler geschriebene Callback-Routine
// für alle Prozessoren auf mit RCX = Argument.
// Blockiert (per Eigenbau-Spinlock) den aktuellen Prozessor (Thread)
// bis alle Prozessoren den Kode ausgeführt haben.
static void EachProcessor(void(*Callback)(void*),void* Arg) {
ULONG i;
volatile KAFFINITY a;
a=KeQueryActiveProcessors();
if (a==1) Callback(Arg); // Einzelprozessorsystem (Abk.)
else{
KAFFINITY m;
KDPC *Dpc,*pDpc;
Dpc=ExAllocatePoolWithTag(NonPagedPool,sizeof(KDPC)*MAXIMUM_PROCESSORS,'tplE');
if (!Dpc) return;
for (i=0,m=1,pDpc=Dpc; a>=m && i<MAXIMUM_PROCESSORS; i++,m<<=1,pDpc++) if (a&m) {
// Für den aktiven Prozessor direkt aufrufen, sonst unnötiges Kuddelmuddel mit IRQL
if (i==KeGetCurrentProcessorNumber()) EachProcessorDpc(NULL,Callback,Arg,(void*)&a);
else{
KeInitializeDpc(pDpc,EachProcessorDpc,Callback);
KeSetTargetProcessorDpc(pDpc,(char)i);
KeInsertQueueDpc(pDpc,Arg,(void*)&a);
}
}
while (a); // warten bis alle fertig sind! (Aua! Aber besser geht's wohl nicht.)
ExFreePoolWithTag(Dpc,'tplE');
}
}
void Ke386SetIoAccessMap(int flag, const IOPM*iopm) {
_movsq(0x400,tsslist.iopm,iopm);
}
void Ke386QueryIoAccessMap(int flag, IOPM*iopm) {
_movsq(0x400,iopm,tsslist.iopm);
}
/*********************************************************************
Set the global IOPM (I/O permission map) so that it is given full I/O access.
If OnFlag is 1, the process is given I/O access.
If it is 0, access is removed.
IRQL is DISPATCH_LEVEL while in startup or shutdown routine!
*********************************************************************/
static void GiveIO(int OnFlag) {
// KIRQL irql=KeRaiseIrqlToDpcLevel();
// __debugbreak();
EachProcessor(OnFlag?SetIOPermissionMap:RevertTSS,NULL);
// KeLowerIrql(irql);
}
/*********************************************************************
Release any allocated objects. Will be called on "sc stop giveio".
*********************************************************************/
static void GiveioUnload(const DRIVER_OBJECT*DO) {
UNICODE_STRING us;
RtlInitUnicodeString(&us,L"\\??\\giveio");
IoDeleteSymbolicLink(&us);
RtlInitUnicodeString(&us,L"\\??\\dlportio");
IoDeleteSymbolicLink(&us);
GiveIO(FALSE);
IoDeleteDevice(DO->DeviceObject);
}
/*********************************************************************
Service handler for a CreateFile() user mode call.
Does nothing than succeed. For compatibility to 32 bit drivers only.
*********************************************************************/
static NTSTATUS OnCreate(PDEVICE_OBJECT o, PIRP I) {
// __debugbreak();
GiveIO(TRUE); // Re-enable I/O passthrough, in case of loss (150625)
I->IoStatus.Information = 0;
I->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(I,IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
// Sorry, I couldn't get _any_ OnPower notification when pressing
// the Sleep button on my desktop computer.
// Furthermore, processor state was still preserved,
// no need for GiveIO(TRUE). heha, 150626
static NTSTATUS OnPower(PDEVICE_OBJECT o, PIRP I) {
IO_STACK_LOCATION*irpStack;
PoStartNextPowerIrp(I);
irpStack=IoGetCurrentIrpStackLocation(I);
// __debugbreak();
if (irpStack->MinorFunction==IRP_MN_SET_POWER
&& irpStack->Parameters.Power.Type==SystemPowerState
&& irpStack->Parameters.Power.State.SystemState==PowerDeviceD0) {
GiveIO(TRUE); // Re-enable I/O passthrough after hibernate or standby
}
return STATUS_SUCCESS;
}
/*********************************************************************
Driver Entry routine.
This routine is called only once after the driver is initially
loaded into memory. (after "sc start giveio")
It creates a symbolic link to the device driver.
This allows a user mode application to access our driver using
\\.\giveio or \\.\dlportio name.
*********************************************************************/
NTSTATUS DriverEntry(PDRIVER_OBJECT DO, PUNICODE_STRING regpath) {
PDEVICE_OBJECT dev;
NTSTATUS status;
UNICODE_STRING uniNameString, uniDOSString;
// Set up device driver name and device object.
RtlInitUnicodeString(&uniNameString,L"\\Device\\giveio");
status = IoCreateDevice(DO,0,&uniNameString,FILE_DEVICE_UNKNOWN,0,FALSE,&dev);
if (!NT_SUCCESS(status)) return status;
GiveIO(TRUE); // give all processes I/O access
RtlInitUnicodeString(&uniDOSString,L"\\??\\giveio");
status = IoCreateSymbolicLink (&uniDOSString,&uniNameString);
if (!NT_SUCCESS(status)) return status;
RtlInitUnicodeString(&uniDOSString,L"\\??\\dlportio");
status = IoCreateSymbolicLink (&uniDOSString,&uniNameString);
if (!NT_SUCCESS(status)) return status;
// Initialize the Driver Object with driver's entry points.
// We require the Create and Unload operations.
DO->MajorFunction[IRP_MJ_CREATE]=OnCreate;
DO->DriverUnload = GiveioUnload;
// For surviving sleep mode, Power notifications must be catched. (?)
DO->MajorFunction[IRP_MJ_POWER]=OnPower;
return STATUS_SUCCESS;
}
Detected encoding: UTF-8 | 0
|