Last edit: 05-03-17 Graham Wideman


gwiopm.pas and gwiopm.sys documentation
Article created: 98-06-01
Minor edits: 98-06-18


The basic model is shown below:

iopm_tech.gif (9789 bytes)

The keys to the whole picture are the kernel functions highlighted in yellow.   These are what our driver uses to command the NT kernel to install our desired I/O permissions map.  (Some comments on this below.)

The unit gwiopm.pas contains an object GWIOPM_Driver with a number of methods to install the driver, prepare an I/O permissions map, hand that map to the driver, and then from driver to kernel.  

An I/O permissions map is 8K bytes in size, with each bit controlling access to one port in the x86's 64K I/O port address space. Bit 0 of Byte 0 controls I/O address 0, and so on. A "1" bit value disables access, a "0" value enables access.

There are other auxiliary functions to provide some diagnostics and general hacking satisfaction.

Task and GWIOPM_Driver function Description
Connect to Service Control Manager  
Open and close connection to Service Control Manager. Basic function of OpenSCM is to get a handle to the SCM, which is retained by the GWIOPM_Driver object for later use in the driver management functions....
Manage Driver  
Install(newdriverpath: string): DWORD;
Start: DWORD;
Stop: DWORD;
Remove: DWORD;
Functions to install, start, stop and remove driver.   Install() takes either a driver path or '' for default (gwiopm.sys in the application's home directory.)
Device functions  
DeviceOpen: DWORD;
DeviceClose: DWORD;
First you must open a device. This fetches a device handle for the future use of GWIOPM_Driver's other driver functions. (Note that the device handle is different from the SCM handle).  Later, you must close the device if you want to remove the driver. Note that closing the device or removing the driver does not remove the IOPM that you may have provided to the NT kernel (though presumably the kernel knows to forget about it after the application using it quits?)
Test functions  
    (var RetVal: DWORD): DWORD;
    (var RetVal: DWORD): DWORD;
Diagnostics to check that driver is installed and functioning. READ_TEST returns a RetVal of $123 while READ_VERSION returns a decimal number somewhere over 100 for version 1.x
Manipulate driver's local IOPM (LIOPM)  
  (Addr: Word; B: byte): DWORD;
Clear the driver's preparatory map array, or set specific bytes to particular values.
  (Addr: Word; var B: byte): DWORD;
Fetch either a particular byte, or the whole map from the driver. Note that this is the driver's map-in-preparation, not the one that the kernel is actually using.
  (BeginPort: word; EndPort: word; 
   Enable: Boolean): DWORD;
Enable/disable bits for specific ports. (Preferable to calling IOCTL_IOPMD_SET_LIOPM if you only need to set or change a few bits.)
Interact with kernel IOPM (KIOPM)  
Give the driver's "local" (preparatory) map to the kernel and make it the active one for our process.
Tell kernel to forget about our process-specific map.
Readback the kernel's map to the driver. (If you then use GET_LIOPMB your app can see the map the kernel is currently using.)
Return values: Functions generally return ERROR_SUCCESS or other error code that can be examined with the ErrorLookup function
ErrorLookup(ErrorNum: DWORD): string;

Kernel Functions

The NT kernel functions that are used by the gwiopm driver are poorly documented, so their exact function is a matter of some speculation. They were partly sleuthed by Dale Roberts, and I have experimented with them a little more. So here's what I think they do:

Kernel function Description or speculation
Ke386IoSetAccessProcess(PEPROCESS, int);
This provides the identity of the current process (ie: your application) to the kernel, and apparently lets the kernel know that we want to use a special I/O permissions map for this process (presumably the kernel needs to reserve us some space.)  The int argument apparently enables (1) or disables (0) the special map (or blanket enables/disables I/O for this process, I'm not sure which.)
Ke386SetIoAccessMap(int, IOPM *); 
This is the function that the driver uses to provide a new map (8K bytes) to the kernel.  The int argument chooses between:
"1":  copy our map... or...
"0": fill kernel's map with 0xFF"
Note that a 1 bit in the map disables access to a particular port, so the int=0 option effectively clears the kernel's map to all-disabled.
Ke386QueryIoAccessMap(int, IOPM *);
This function copies the kernel's map back to the driver's local map buffer.  I have no idea what the int argument does, but when set to 1 it works. Perhaps set to 0 it clears the driver's map to 0xFF's.

In each case you can look at the gwiopm.c source to see how I used them.  This follows how Dale Roberts used them, with a little bit of extra experimentation on the Query function.

Further Adventures...

Client-specific IOPM: The gwiopm.sys driver was written with only a single client application in mind.  In fact the whole idea of bypassing NT's resource arbitration/allocation philosophy and  programming directly to the I/O ports presupposes that only one application (or applications that are very friendly) will be using the driver.   However, it might make some sense to modestly redesign gwiopm so that the local IOPM table is client-app specific, rather than global for all clients of the driver. This could be done by defining a so-called "device extension" to hold the client-by-client port-enable info. See the Art Baker reference or the NT DDK samples.

Go to:  wideman-one