Copyright © 2004 by Printing Communications Assoc., Inc. (PCAUSA). All rights reserved
This is Part 3 of an ongoing series of articles that focus on extending the Microsoft® Windows® Driver Development Kit (DDK) PassThru NDIS Intermediate (IM) driver sample. Previous articles in this series were:
These prior articles as well as their companion source code can be found in the Windows Driver Developer's Digest Archives.
This article will describe building the IP Address Blocking extended PassThru driver and it's companion user-mode application (IOCTL design, by Thomas, from Part 2 of this series) to run on Windows XP 64-Bit Edition for 64-Bit Extended Systems (AMD Athlon 64).
Adapting drivers to support 64-bit Windows is a timely subject because 1.) the cost of AMD Athlon 64 desktops has dropped below US$1,500.00 and 2.) Microsoft has now publicly released a Windows XP 64-Bit preview version free for the download. In a bit of a turn-around, Intel has now committed to cloning the AMD 64-bit instruction set in a new processor.
Before long 64-bit extended systems hardware should be available in most online and walk-in computer stores. It really won't be long before customer's ask:
"Will your driver work on 64-bit Windows?".
I hope that this article will show that the effort required to adapt a driver to support 64-bit extended systems (AMD Athlon and Opteron) is not excessive.
Your driver
really should support 64-bit Windows!!!
This article
does not address supporting 64-bit Intel Itanium–based systems.
Of course you will need a 64-bit development system. PCAUSA acquired a Compaq Presario 8765C desktop when it became available online (Disclosure: PCAUSA has stock in Hewlett-Packard). Other suitable AMD Athlon 64 systems should be available by the time you read this article. I believe that there is even an Athlon 64 notebook in the works...
The PC came with Windows XP Professional 32-Bit edition pre-installed on one large partition. The first thing that I did was to repartition the hard drive to 1.) preserve the pre-installed Windows XP 32-bit OS and 2.) create separate partitions for installing Windows 64-Bit edition operating systems and partitions for source code that would be accessed by all OS's. This will allow the PC to boot to either 32-bit or 64-bit Windows.
Then I installed Windows XP 64-Bit Edition build 3790 from the Windows DevCON 2003 CD. If you don't have this CD you can order a CD (or download) a public pre-release of this operating system from Microsoft. See Windows XP 64-Bit Edition for 64-Bit Extended Systems Customer Preview Program.
Installation of Windows XP for 64-Bit Edition Extended Systems was uneventful on this platform.
The build environment is part of the Windows Server 2003 DDK (Build 3790). Be sure to install the components related to building AMD 64-Bit drivers. When you have successfully installed the DDK you should have these build environments available:
The Windows Server 2003 build environments can be used to build NDIS drivers for Windows XP.
You should test your AMD64 build environment by building the stock PassThru driver from the Build 3790 DDK. It should build with no errors. (Nice job, Microsoft!)
If you attempt to build the extended PassThru driver from Part 2 of this article series you will encounter three errors. (Shame on you, Thomas!)
There are three errors reported when you initially attempt to build the PassThru Part 2 driver. However, these errors are trivial to fix:
WAS:
DBGPRINT(( "PtDerefAdapter: Adapter: 0x%8.8X\n", pAdapt ? (ULONG )pAdapt : 0 ));
SHOULD BE:
DBGPRINT(( "PtDerefAdapter: Adapter: 0x%p\n", pAdapt ? pAdapt : 0 ));
WAS:
DBGPRINT(( "DevDerefOpenContext: Context: 0x%8.8X\n", pOpenContext ? (ULONG )pOpenContext : 0 ));
SHOULD BE:
DBGPRINT(( "DevDerefOpenContext: Context: 0x%p\n", pOpenContext ? pOpenContext : 0 ));
WAS:
if( DispatchLevel )
NdisDprAcquireSpinLock(&pAdapt->Lock);
else
NdisAcquireSpinLock(&pAdapt->Lock);
SHOULD BE:
if( DispatchLevel )
{
NdisDprAcquireSpinLock(&pAdapt->Lock);
}
else
{
NdisAcquireSpinLock(&pAdapt->Lock);
}
The first two errors are identical. They relate to generating debug messages that display the memory address of two structures. Converting these to use the "%p" print format provides a solution that will work on both 32-bit and 64-bit drivers.
The problem in Filter.c at line 502 is evidently related to MACROs and is ignored by the x86 compiler but caught with the AMD64 compiler.
After making these three small changes the extended PassThru Part 2 driver builds for AMD64 without errors.
The 64-bit PassThru driver is installed exactly the same way as the 32-bit driver. Simply copy the two .INF files and the 64-bit driver executable to a folder that can be accessed from the Windows 64-Bit OS. Then use the Network Control Panel to install the PassThru service.
You will see the usual "unsigned driver" warnings, and the driver should installed successfully.
Microsoft provides 64-bit versions of their debugging tools. These install and are used exactly as their 32-bit counterparts. Two machines (64-bit target and 32-bit host) are required to support 64-bit kernel-mode driver debugging. However, the debugging of 64-bit user-mode applications can be performed standalone on the 64-bit host.
You can download the Microsoft Debugging Tools for Windows Native 64-bit beta package from this URL:
http://www.microsoft.com/whdc/ddk/debugging/default.mspx
Because of the simplicity of the PassThru Part 2 IOCTL API we can actually test it right away. Simply copy the 32-bit TestIOCTL application to the 64-bit workstation and run it. It will work exactly the same as the 32-bit counterpart.
Do understand that you will need to enumerate the PassThru bindings and edit the NDIS adapter name in the address blocking list file. This is described in the TestIOCTL User's Guide.
Click here for the TestIOCTL User's Guide.
Actually, building 64-bit applications is more difficult then it should be. At the time of this writing:
The only viable build environment for 64-bit applications is actually the DDK build environment.
The original Part 2 TestIOCTL was developed as a "MFC Console Application" and used a mixture of stdio and C++ iostream calls to write to the console. Clearly this cannot be built as-is for 64-bit Windows. We copied the TestIOCTL source code and header files to a new TestPart3 folder and then revised the code to be built under the DDK. These were the basic steps:
The revised files are substantially similar to the original Part 2 version. The revised TestIOCTL application in the TestPart3 folder application can now be built under the DDK as a 32-bit application or as a 64-bit application.
At this point you can use either the 32-bit TestIOCTL or the 64-bit TestIOCTL application to control the 64-bit PassThru driver.
In some cases you may not actually need a 64-bit application. If your driver supports both 32-bit and 64-bit applications, then you can use a 32-bit application to provide a "pretty face". You may need a companion 64-bit faceless Windows service or Win32 application to perform I/O intensive "heavy lifting".
Of course your own driver may not be as easy to port to 64-bits as PassThru. Here are some issues that I encountered when porting other PCAUSA drivers to 64-bit Windows:
See Resources for 64-Bit Driver Development below.
Search the DDK help for "64-bit Windows" (in quotes) and then select the New Data Types topic.
NDIS uses a lot of integer-size variables. For example, NDIS_STATUS is actually defined as an "int". In addition, "enum" types such as NDIS_REQUEST_TYPE are integer size. Obviously these variables will have different sizes in 32-bit and 64-bit builds.
The difference is size of pointers should be obvious.
A lot of alignment and porting issues can be caught early if you include length checks in your driver's IOCTL handler before actually accessing I/O data.
For example, if you are passing a structure to your driver, have your driver calculate the expected length of the structure (using sizeof) and compare it to the input buffer length. If there is a mismatch, fail the call with ERROR_BAD_PARAMETER. During debugging this check will help identify problems. After debugging, these checks will improve the overall security of your driver by reducing vulnerability to malicious malformed data.
The extended PassThru driver performs these sorts of checks in the IOCTL_PTUSERIO_SET_IPv4_BLOCK_FILTER handler.
The 64-bit compiler will identify some data type mismatches that are overlooked by the 32-bit compiler. I found cases where I had mismatched BOOL (an "int") with BOOLEAN (an unsigned character).
If you elect to develop a driver that can concurrently support both 32-bit and 64-bit user-mode applications then you may encounter situations in IOCTL handling where is is necessary to know whether the caller is a 32-bit or a 64-bit application. Use IoIs32bitProcess to make this distinction.
If your API passes structures from user-mode to kernel-mode, then you may need to provide different structure definitions depending on whether on the "bitness" of the caller. IoIs32bitProcess would branch the code to one path for a 64-bit caller and to a different path for a 32-bit caller.
In each process path it may be necessary to use different representations of the same data structure. This is called "thunking".
In the sample below the struct _XYZ_NDIS_REQUEST structure on the left is used by:
However, this structure cannot be used when the 64-bit driver is called by a 32-bit application. Many of the fields are of integer or pointer size and must be handled as a special case in the 64-bit driver (colorized red). To accommodate this the 64-bit driver can use the alternate structure representation on the right. The struct _XYZ_NDIS_REQUEST_32 structure described the data passed from the 32-bit application in terms of explicit data types that the 64-bit driver can handle as a special case.
Used By 64-Bit Driver if IoIs32BitProcess
is FALSE (Called by 64-Bit Application) |
Used By 64-Bit Driver if IoIs32BitProcess
is TRUE (Called by 32-Bit Application) |
// // Normal XYZ_NDIS_REQUEST Structure // --------------------------------- // Used by applications and the driver // except in the case described to the // right. // typedef struct _XYZ_NDIS_REQUEST { UCHAR MacReserved[16]; NDIS_REQUEST_TYPE RequestType; union _XYZ_DATA { struct _XYZ_QUERY_INFORMATION { NDIS_OID Oid; PVOID InformationBuffer; UINT InformationBufferLength; UINT BytesWritten; UINT BytesNeeded; } QUERY_INFORMATION; struct _XYZ_SET_INFORMATION { NDIS_OID Oid; PVOID InformationBuffer; UINT InformationBufferLength; UINT BytesRead; UINT BytesNeeded; } SET_INFORMATION; } DATA; } XYZ_NDIS_REQUEST;
|
#ifdef _WIN64 // // Thunking XYZ_NDIS_REQUEST Structure // ----------------------------------- // This structure is used by 64-bit driver // when handling request from 32-bit // application. // typedef struct _XYZ_NDIS_REQUEST_32 { UCHAR MacReserved[16]; NDIS_REQUEST_TYPE RequestType; union _XYZ_DATA_32 { struct _XYZ_QUERY_INFORMATION_32 { NDIS_OID Oid; VOID*POINTER_32 InformationBuffer; UINT32 InformationBufferLength; UINT32 BytesWritten; UINT32 BytesNeeded; } QUERY_INFORMATION; struct _XYZ_SET_INFORMATION_32 { NDIS_OID Oid; VOID*POINTER_32 InformationBuffer; UINT32 InformationBufferLength; UINT32 BytesRead; UINT32 BytesNeeded; } SET_INFORMATION; } DATA; } XYZ_NDIS_REQUEST_32; #endif // _WIN64 |
If you are compiling code that has a long history, then there may be some useless (but harmless) code around. The 64-bit compiler will give warnings such as "statement is always TRUE" when these are encountered. Here is one example:
ULONG AVariable = 0;// Lots of code. Forgot that AVariable was a ULONG...if( (AVariable >= 0) && (Some other conditions...) ) { // Do Something... }
The statement (AVariable >= 0) will generate a warning "statement is always TRUE" that will fail the 64-bit build. You need to remove the unnecessary test to remove the warning.
These sorts warning are simple to fix after you encounter them for the first time.
Search for "64-bit Windows" (in quotes) to focus on the top-level information of most interest.
Visit the URL http://www.microsoft.com/whdc/winhec/pres03.mspx. Look for these topics System Design & Innovation section:
Visit the AMD website at http://www.amd.com. Select the Processors tab at the top of the page, and then browse Resources for Developers. [Ed.: we're departing from the normal WD-3 rule about not referencing commercial sites because there is no simple URL that leads directly to the Developer Resources page on the AMD web site. We are not hereby endorsing AMD products.]
This article and the companion code are intended to be used for the benefit of the reader of this article. The companion code is intended to help the reader derive and improve their own NDIS Intermediate driver for Windows platforms.
Here is the general form of the Copyright Notice found in the companion code for this article:
Companion Sample Code for the Article
"Extending the Microsoft PassThru NDIS Intermediate Driver"
Portions Copyright (c) 1992-2000 Microsoft Corporation; used by permission.
Portions Copyright (c) 2003-2004 Printing Communications Associates, Inc. (PCAUSA)
The right to use this code in your own derivative works is granted so long as
This product includes software developed by PCAUSA. The name of PCAUSA may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Portions of the IP Blocking Driver by Thomas include the following additional notice:
Copyright (c) 1982, 1986, 1993 The Regents of the University of California. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This article is:
Copyright (c) 2004 Printing Communications Associates, Inc. (PCAUSA). All rights reserved.
PCAUSA does not grant the right to redistribute or publish this article without written permission.
It is certainly intended that you should be able to incorporate the ideas and code presented in this article into your own code. At the same time, for a free code like this it is essential that you understand that the authors do not provide any warranty whatsoever that the sample code is fit for any purpose whatsoever.
In other words: This article and the accompanying sample code are provided for educational purposes only. Use at your own risk.
[ Click here to download PassThruEx Part 3 source code... ]
When you unzip the sources the directory tree will be like this:
\PCADev \NDISIM \PassThruEx \Part 3 - Root for PassThruEx Part 3 Sources \TestIOCTL- TestIOCTL Application from Part 2 \TestPart3 - TestIOCTL Application Revised for DDK Build \Driver - PassThru Part 3 Sources \B2Winet - BSD 5.0 inet Headers Adapted for Windows
Thomas F. Divine is founder of PCAUSA, a company which has been serving the Windows device driver community since 1992. PCAUSA licenses network device driver samples that illustrate specialized kernel mode programming technologies such an NDIS Intermediate drivers, TDI Clients and a variety of network data filtering techniques. You can reach him at wd-3.tdivine@pcausa.com.