From: <lab...@us...> - 2004-03-10 20:45:12
|
Update of /cvsroot/opengtoolkit/portIO/c_source In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv21446/c_source Added Files: Description.htm Log Message: Added a first draft of documentation of the device driver and its operation --- NEW FILE: Description.htm --- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"> <title>Open G Port IO Driver</title> </head> <body> <br> <big style="font-weight: bold;"><big><img style="width: 36px; height: 36px;" alt="" src="file:///D:/CVS/OpenG/development/portIO/ogportio.bmp"> Open G Port IO Driver<br> </big><br> Introduction<br> </big><br> For Windows NT based systems (NT 4, 2000, XP, 2003) access to hardware resources from user space (eg. any application) is restricted to improve system security. If an application tries to access such resources directly, the CPU indicates a Protection Fault which then is catched by the Windows NT kernel. The kernel then displays a dialog (or optionally offers to start a system debugger) and terminates the process which caused the Protection Fault.<br> <br> If you want to program prototype hardware, this restriction is rather strong and it would be nice to loosen that protection partly for such situtions. There are basically two options to acess the port IO address range. The first is letting a device driver perform the actual port IO access, as device drivers run in the privileged kernel space and do not have the same limitations as applications in the user space. This is the method used by most device drivers for specific hardware. For such drivers the performance loss caused by a device driver call is not really important, as the device driver provides a high level API which can for instance return an entire buffer of data per single call. The access to the individual addresses (sometimes a driver needs to program dozensor much more of registers for a single operation) are all done inside the single device driver call, so that the performance loss caused by switching between the user space and kernel space context is minimal, in comparison to the actual time needed for the device driver call itself. For our port driver however this is different. A port address read itself typically takes less than 1 us while the time needed to switch from user space to kernel space and back is typically somewhere around 100us on an older 866MHz Pentium system. You can see that while the IO port access itself is quite fast the necessary context switches are expensive. Therefore it would be interesting if we had a possibility to enable port address access dirctly from the user application.<br> <br> Fortunately there is a possibility, as the x86 architecture controls access to the IO port address space by an IO Permission bitMap (IOPM). This bitmap contains 1 bit per port address and as the IO port address space of the x86 goes from 0x0000 - 0xFFFF this bitmap is 8096 bytes long.<br> <br> <span style="font-weight: bold;">NOTE</span>: This method is not really recommended for production grade systems as a misbehaving user application can easily trash the entire system (as I have found during development :-) or even damage the hardware or destroy information in the system or on it's connected devices.<br> <big style="font-weight: bold;"><br> Manipulating the IOPM (IO Permission bitMap)</big><br> <br> To understand how this can work we also need to explain a little more about the x86 CPU architecture. These CPUs have four so called rings, which can be assigned to certain subsystem of an operating system. Ring 0 has the highest access rights and can basically access any opcodes and resources in the x86 CPU. The Windows NT kernel runs in ring 0. Ring 3 has the lowest access rights and some of the CPU opcodes as well as certain CPU registers are not accessible while the CPU runs in ring 3. The Windows NT user space in which all applications run is assigned to ring 3.<br> <br> If a particular bit is set in that IOPM the according IO port address is protected and the processor will generate a software interrupt when running in ring 3. Windows NT will see this interrupt as exception and usually notify the user and terminate the current process. The Windows NT kernel maintains the IOPM and makes sure that it is always setup correctly for the current process. By default all processes have a default IOPM which disables access to all IO port addresses completely.<br> <br> However the Windows NT kernel exports some undocumented functions to modify the IOPM for a particular process. With these functions a device driver can allow a particular process to access some or all IO port addresses directly from user space. These functions are:<br> <br> NTKERNELAPI void Ke386SetIoAccessMap(int, IOPM *pBitmap);<br> NTKERNELAPI void Ke386QueryIoAccessMap(int, IOPM *pBitmap);<br> <br> Ke386SetIoAccessMap will copy the IOPM to the TSS, and Ke386QueryIoAccessMap witll read it from the TSS. The first parameter of these functions is not documented and should be set to 1 to work as expected.<br> <br> NTKERNELAPI void Ke386IoSetAccessProcess(PEPROCESS pProc, int install);<br> NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(IN ULONG ulProcId, OUT PEPROCESS * pEProcess);<br> <br> Ke386IoSetAccessProcess adjusts the IOPM offset pointer of the CPU to point to the current IOPM. The second parameter specifies if the IOPM should be installed or removed. As Ke386IoSetAccessProcess expects a pointer to an internal kernel structure identifying the process to change the IOPM pointer for, we have another undocumented function PsLookupProcessByProcessId to translate a user space process ID into the according kernel structure pointer.<br> <br> As the necessary interfaces are all protected and only accessible from kernel space you do need a device driver which can run in the kernel space and access the necessary routines and resources, independant if you want to let the device driver do the IO port access or provide functions to manipulate the IOPM to allow direct user space access to certain IO port addresses.<br> <br> <big>Talking to the Device Driver (User Mode APIs)</big><br> <br> To access the device driver, a user mode API has been developed as a DLL. This DLL provides different functions to control access to the IO port address range. It both allows to access IO port addresses trough the device driver (which is the classical way as implemented by the Port IO functionality distributed with LabVIEW 7.0) or directly using the according x86 opcodes . However to be able to use the direct access functions you will first have to enable the according addresses. Be aware that if you want to access port 0x220 as 16 bit value, you will for instance have to enable port address 0x220 with a length of 2.<br> Failing to enable a port before accessing it with the direct access function will generate a General Protection Fault error under LabVIEW 6.0 where as LabVIEW 6.1 and higher will catch the according exceptions itself and display a dialog box to that fact and suggesting to restart LabVIEW. If you are sure that this dialog was caused by such an unprivileged IO port access you can safely ignore the dialog as no modifications to any LabVIEW application data has been caused by the DLL.<br> The DLL controls itself if it is running on Windows NT based systems and behaves accordingly. For non-NT systems (9x/ME) the DLL does not need any device drivers and will always directly access the IO port addresses. The functions to enable and disable certain port addresses are just empty operations on those systems. This allows applications to always use the same function calls (LabVIEW VIs) and still run correctly on Win9x/ME systems.<br> On Windows NT systems the DLL checks before each call if the device driver is already installed and running, and attempts to start and eventually install the device driver. This will however only work if the current user has administrator rights at that moment. Once the driver is installed no administrator rights are necessary anymore to use the device driver.<br> <br> </body> </html> |