Debugging NDIS Drivers

Windows Platform Design Notes

Design Information for the Microsoft® Windows® Family of Operating Systems

Debugging NDIS Drivers

Abstract

This paper provides information about debugging Network Driver Interface Specification (NDIS) drivers for the Microsoft® Windows® family of operating systems. It provides guidelines for the NDIS driver developer to identify commonly encountered issues in network drivers and specifies best practices that will help the driver developer avoid network driver issues in the first place. This paper also provides information on how to use Ndiskd.dll, the NDIS kernel debugger extension, to gather information about the state of NDIS and the NDIS drivers.

Contents

Introduction. 3

NDIS Overview.. 3

Using Ndiskd.dll to Debug NDIS Drivers. 4

Debugger Extensions. 6

NDIS Tracing. 9

NDIS Bug Checks. 10

PnP Overview.. 11

Debugging a Miniport Initialization Problem.. 12

Debugging the Failure of a Miniport to Halt13

Debugging Power Management Issues. 14

Debugging Interrupt Storms. 15

MiniportReset Overview.. 16

Best Practices for Developing Miniport Drivers. 16

Tools – Using NDISTest and Driver Verifier16

Using Debug Messages. 16

Tracking Spin Locks. 16

Best Practices for Developing Intermediate Drivers. 17

Common Intermediate Driver Issues. 17

Best Practices for Developing Protocol Drivers. 18

Call to Action and Resources. 18

This is a preliminary document and may be changed substantially prior to final commercial release of the software described herein.

The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication.

This White Paper is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS DOCUMENT.

Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

Unless otherwise noted, the example companies, organizations, products, domain names, e-mail addresses, logos, people, places and events depicted herein are fictitious, and no association with any real company, organization, product, domain name, e-mail address, logo, person, place or event is intended or should be inferred.

® 2003 Microsoft Corporation. All rights reserved.

Microsoft, Windows, and Windows Server are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their respective owners.

Introduction

This paper provides information about debugging NDIS drivers for the Microsoft® Windows® family of operating systems. It also provides coding practices that driver developers can use to avoid common problems encountered by NDIS drivers such as initialization failures in NDIS miniports, NDIS Intermediate drivers whose miniports do not get halted, and other common issues reported to the NDIS team at Microsoft. The paper discusses:

  • Ndiskd.dll—NDIS kernel debugger extensions
  • NDIS tracing
  • PnP overview
  • Power Management overview
  • NDIS intermediate drivers
  • NDIS protocol drivers

Note: The information presented in this paper is applicable to Microsoft Windows Server™ 2003 driver development. Most of the information also applies to Microsoft Windows XP and Microsoft Windows 2000.

NDIS Overview

This paper assumes that you are familiar with the basic NDIS model. In addition to the miniports and protocols that have been discussed in the Microsoft Windows Driver Development Kit (DDK), this paper introduces the MOpen structure that represents a binding between a protocol and a miniport. The model, including the MOpen structure, appears in figure 1.

NDIS_diagram_including_the_MOpen_structureFigure 1: NDIS diagram including the MOpen structure

Using Ndiskd.dll to Debug NDIS Drivers

Developers and testers at Microsoft Corporation use Ndiskd.dll to quickly analyze potential network driver issues. Ndiskd.dll is a part of the Microsoft Debugging Tools package for kernel-mode debugging. Ndiskd.dll runs on both WinDBG.exe and Kd.exe, and it provides basic and detailed information about a miniport driver and the protocol drivers that are bound to it.

The most commonly used Ndiskd.dll commands are !miniports and !miniport <MiniportAdapterHandle>. Use the !miniports command to print all of the NDIS miniports that are present in the system. Use the !miniport <MiniportAdapterHandle> command to print detailed information about a miniport adapter, including the protocols it is bound to and the miniport’s PnP and power management states.

The following example shows how to get detailed information about a particular protocol that is bound to the Sample Ethernet Driver miniport. The Sample Ethernet Driver is a real NDIS miniport driver that has been given a fictitious name.

0: kd> !miniports
NDIS Driver Verifier level: 0
NDIS Failed allocations   : 0
Miniport Driver Block: 80d883c0, Version 0.0
Miniport: 80dbb130 1394 Net Adapter
Miniport Driver Block: 80dc7c30, Version 4.25
Miniport: 80dc76f0Sample Ethernet Driver

To print detailed information about the Sample Ethernet Driver, use the !miniportcommand. The pointer in bold in the preceding example is the argument to !miniport.

0: kd> !miniport 80dc76f0
Miniport 80e16130 : Sample Ethernet Driver
AdapterContext : 80cd52c0
Flags         : 2c412048
BUS_MASTER, SG_LIST, IGNORE_TOKEN_RING_ERRORS
RESOURCES_AVAILABLE, SUPPORTS_MEDIA_SENSE,
DOES_NOT_DO_LOOPBACK, MEDIA_CONNECTED,
PnPFlags       : 00210000
RECEIVED_START, HARDWARE_DEVICE,
InternalResetCount   : 0000
MiniportResetCount   : 0000
References           : 3
UserModeOpenReferences: 0
PnPDeviceState       : PNP_DEVICE_STARTED
CurrentDevicePowerState : PowerDeviceD0
Bus PM capabilities
DeviceD1:       1
DeviceD2:       1
WakeFromD0:     0
WakeFromD1:     1
WakeFromD2:     1
WakeFromD3:     1
SystemState     DeviceState
PowerSystemUnspecified  PowerDeviceUnspecified
S0          D0
S1          PowerDeviceUnspecified
S2          PowerDeviceUnspecified
S3          D3
S4          D3
S5          D3
SystemWake: PowerSystemUnspecified
DeviceWake: PowerDeviceUnspecified
Current PnP and PM Settings:         : 00000030
DISABLE_WAKE_UP, DISABLE_WAKE_ON_RECONNECT,
Allocated Resources:
IO Port: 0000ec00, Length: 80
Memory: fe004000, Length: 80
Interrupt Level: 44, Vector: 44
Translated Allocated Resources:
IO Port: 0000ec00, Length: 80
Memory: fe004000, Length: 80
Interrupt Level: 9, Vector: 1a3
MediaType     : 802.3
DeviceObject   : 80e16030, PhysDO : 80e939a0 Next DO: 80e939a0
MapRegisters   : 00000000
FirstPendingPkt: 00000000
DriverVerifyFlags : 00000000
Miniport Interrupt : 80cd5530
Miniport Open Block Queue:
80d066b0: Protocol 80ceb978 = TCPIP, ProtocolBindingContext 80d21a50
80db44c0: Protocol ffb54478 = NDISUIO, ProtocolBindingContext 80ceff58

Use the !mopen command to print detailed information about an Mopen structure, or binding, between the Sample Ethernet Driver and a protocol. Use the pointers in bold from the preceding example as arguments for the !mopen command.

0: kd> !mopen 80db44c0
Miniport Open Block 80db44c0
Protocol ffb54478 = NDISUIO, ProtocolContext 80ceff58
Miniport 80dc76f0 = Sample Ethernet Driver
MiniportAdapterContext: 80cd52c0
Flags                 : 00000000
References           : 1

0: kd> !mopen 80d066b0
Miniport Open Block 80d066b0
Protocol 80ceb978= TCPIP, ProtocolContext 80d21a50
Miniport 80dc76f0 = Sample Ethernet Driver
MiniportAdapterContext: 80cd52c0
Flags                 : 00000000
References           : 1

To print information about the protocol structures use the !protocol command. Use the pointer in bold from the preceding example as the argument to the !protocol command.

0: kd> !protocol ffb54478
Protocol ffb54478 : NDISUIO
RootDeviceName is \DEVICE\{9C358E9A-BC5E-4A71-A8B6-993A0D3B404D}
RefCount 2
Open 80db44c0 - Miniport: 80dc76f0 Sample Ethernet Driver
BindAdapterHandler   fb1a6e56, UnbindAdapterHandler fb1a6a30
PnPEventHandler     fb1a67c2, UnloadHandler         00000000
OpenAdapterComplete fb1a6224, CloseAdapterComplete fb1a6242
SendCompleteHandler fb1a78c0, TransferDataComplete fb1a75d2
ReceiveHandler       fb1a770c, ReceivePacketHandler fb1a7636
ReceiveComplete     fb1a6514, StatusHandler         fb1a64c4
StatusComplete       fb1a6514
AssociatedMiniDriver 00000000
Flags         : 00000000

The preceding example shows how to use the list of miniport drivers in the system to get detailed information about the protocols that are bound to a miniport adapter. The example printed four sets of information:

  • Detailed information about each NDIS miniport adapter in the system.
  • Specific information about the Sample Ethernet Driver.
  • Detailed information about each of the Mopen structures, or bindings, on the Sample Ethernet Driver miniport.
  • Detailed information about the NdisUio protocol.

Note: For detailed information about Ndiskd.dll, see the Help file provided with the Microsoft Debugging Tools, and the Microsoft Windows Driver Development Kit (DDK).

Debugger Extensions

Three useful debugger extensions exist that are not well known:

  • !pkt
  • !findpacket -p
  • !stacks

Ndiskd.dll includes the !pkt and !findpacket commands. The !stacks command is part of the Microsoft Debugging Tools package and is not included in Ndiskd.dll.

!pkt Command

The !pkt command prints out detailed information about an NDIS_PACKET structure. You can specify a verbosity level to control the amount of information that the debugger prints, as shown in the following code.

kd> !ndiskd.pkt 817b03f8 5
Usage: pkt <pointer to packet> <verbosity>
1-Packet Private, 2-Packet Extension
3-Ndis Reference, 4-Buffer List

To print every NDIS_BUFFER structure in an NDIS_PACKET structure, use a verbosity level of 4:

kd> !ndiskd.pkt 817b03f8 4
NDIS_PACKET at 817b03f8
NDIS_BUFFER at 816bb104
Next           816bc054
Size         20
MdlFlags       4
Process       00000000
MappedSystemVa 816bb124
Start VA       816bb000
ByteCount     e
ByteOffset     124
NDIS_BUFFER at 816bc054
Next           816480d0
Size           20
MdlFlags       4
Process       00000000
MappedSystemVa 816bc074
Start VA       816bc000
ByteCount     14
ByteOffset     74
NDIS_BUFFER at 816480d0
Next           00000000
Size           20
MdlFlags       1004
Process       00000000
MappedSystemVa 81648110
Start VA       81648000
ByteCount     14
ByteOffset     110
MacReserved[]:   00000000     00000000     00000000     00000000
0. TcpIpChecksumPacketInfo = 00000000
1. IpSecPacketInfo = 00000000
2. TcpLargeSendPacketInfo = 00000000
3. ClassificationHandlePacketInfo = 00000000
4. NdisReserved = 00000000
5. ScatterGatherListPacketInfo = 00000000
6. Ieee8021pPriority = 00000000
7. OriginalPacketInfo = 817b03f8
8. PacketCancelId = 00000000
9. MaxPerPacketInfo = 00000000

Packet.Private

PhysicalCount      00000001 Total Length       00000028
Head               816bb104 Tail               816480d0
Pool               817b36a0 Count               00000001
Flags               00000002 ValidCounts         00
NdisPacketFlags     00000080 NdisPacketOobOffset 006c
Private.Flags         : 00000002
Private.NdisPacketFlags: 80
fPACKET_ALLOCATED_BY_NDIS,

The preceding example prints every NDIS_BUFFER structure in a particular NDIS_PACKET structure, as well as the Out-Of-Band(OOB) information in the NDIS_PACKET structure.

!findpacket p Command

The !findpacket -p command finds all the packets that are allocated from a particular packet pool and that are currently in use by the miniport driver. Intermediate (IM) driver developers can use the findpacket -p command to list all of the packets that are allocated by the IM driver and indicated up to the protocols.

The following example shows how to locate all of the packets that are currently allocated in a packet pool. You need to pass the packet pool’s PoolHandle, that was returned to the NDIS driver when it called the NdisAllocatePacketPoolEx function, as the argument to the !findpacket -p command, as shown below.

kd> !ndiskd.findpacket p 816d4e68
Searching Free block <0x816d0000>
Packet at 0x816d0058
Packet at 0x816d0120
Packet at 0x816d01e8
Packet at 0x816d02b0
Packet at 0x816d0378
Packet at 0x816d0440
Packet at 0x816d0508
Packet at 0x816d05d0
Packet at 0x816d0698
Packet at 0x816d0760
Packet at 0x816d0828
Packet at 0x816d08f0
Packet at 0x816d09b8
Packet at 0x816d0a80
Packet at 0x816d0b48
Packet at 0x816d0c10
Packet at 0x816d0cd8
Packet at 0x816d0da0
Packet at 0x816d0e68

!stacks Command

Use the !stacks command to find hung threads and to debug other difficult problems. This extension is present in the Debugger package but is not a part of ndiskd.dll.

You can specify a character string as an argument to the !stacks command. The debugger prints all the threads that contain the character string in their stacks. For example, the command !stacks 2 ndis! causes the debugger to print every thread that contains the “ndis!” string in its stack.

The threads printed by the debugger can provide important clues that will help you solve the problem you are investigating. For example, the output in the following code was taken from an IM driver that was not completing any requests to send packets. When the miniport adapter was disabled, the underlying miniport adapter was never halted as TCP/IP waited for the IM driver to complete the sends. The output, which is the result of the !stacks 2 ndis! command, contains two threads: one that shows TCP/IP waiting for outstanding sends to complete, and another that is an NDIS thread reserved for system use.

0: kd> !stacks 2 ndis!
Proc.Thread   .Thread   Ticks   ThreadState Blocker
[80e76648 System]
4.00003c 80e738a0 0001f02 Blocked   nt!KiSwapContext+0x26
nt!KiSwapThread+0x280
nt!KeWaitForSingleObject+0x249
tcpip!TCPCleanup+0xcc
tcpip!TCPDispatch+0x91
nt!IofCallDriver+0x3f
nt!IopCloseFile+0x27c
nt!ObpDecrementHandleCount+0x121
nt!ObpCloseHandleTableEntry+0x12f
nt!ObpCloseHandle+0x80
nt!NtClose+0x17
nt!_KiSystemService+0xd0
nt!ZwClose+0x11
netbt!DelayedNbtCloseFileHandles+0x4b
netbt!CloseAddressesWi…+0x10b
netbt!NbtNewDhcpAddress+0x69
netbt!TdiAddressDeletion+0x82
TDI!TdiNotifyPnpClientList+0xd7
TDI!TdiExecuteRequest+0x25b
TDI!TdiHandleSerializedRequest+0x1c2
TDI!TdiDeregisterNetAddress+0xb
tcpip!NotifyAddrChange+0x109
tcpip!IPpSetNTEAddr+0xde
tcpip!IPDelNTE+0x39
tcpip!IPDelInterface+0xdf
tcpip!ARPUnbindAdapter+0x70
NDIS!ndisUnbindProtocol+0x151
NDIS!ndisCloseMiniportBindings+0x201
NDIS!ndisPnPRemoveDevice+0x128
NDIS!NdisIMDeInit…DeviceInst…+0x40
passthru+0x2dc3
NDIS!ndisUnbindProtocol+0x151
NDIS!ndisCloseMiniportBindings+0x201
NDIS!ndisPnPRemoveDevice+0x128
NDIS!ndisPnPDispatch+0x15b
nt!IofCallDriver+0x3f
nt!IopSynchronousCall+0xc0
nt!IopRemoveDevice+0x89
nt!IopRemoveLockedDeviceNode+0x15e
nt!IopDeleteLockedDeviceNode+0x32
4.00007c 80e04da0 0000129 Blocked   nt!KiSwapContext+0x26
nt!KiSwapThread+0x280
nt!KeRemoveQueue+0x29b
NDIS!ndisWorkerThread+0x2e
nt!PspSystemThreadStartup+0x2e
nt!KiThreadStartup+0x16

The preceding example shows the benefit of running the !stacks 2 ndis! command, which provides useful clues about the current state of the computer. In the example, you can see that TCP/IP is waiting for an event. It is reasonable to assume that it is waiting for outstanding sends. Later in this paper, you will see how this assumption can be verified.

The second thread printed in the preceding example is reserved for system use.

Use the !stacks 2 <miniport driver name> command to print all the threads with the miniport driver’s name in their stacks.

NDIS Tracing

NDIS has debug messages built into its checked version. By default, these are not printed in the debugger and need to be turned on. Use NDIS tracing to analyze problems that are not in the data path, such as PnP and power-management problems. NDIS tracing works on both free and checked builds of the operating system.

The following example demonstrates how to turn on the NDIS tracing feature in the REQUEST code path at the INFO level within NDIS. There are four levels of debug messages available to the developer: Info, Warn, Err and Fatal. The Info level is the most verbose, and the Fatal level is the least verbose. (The Log level has not been implemented.)

0: kd> !ndiskd.dbglevel
Current setting:FATAL
Available settings:
INFO LOG WARN ERR

0: kd> !ndiskd.dbglevel INFO

0: kd> !ndiskd.dbgsystems
Current settings:
Available settings:
INIT CONFIG SEND RECV PROTOCOL
BIND BUS_QUERY REGISTRY MEMORY FILTER
REQUEST WORK_ITEM PNP PM OPEN
LOCKS RESET WMI NDIS_CO REFERENCE

0: kd> !ndiskd.dbgsystems REQUEST BIND
Arg = REQUEST
Arg = BIND

0: kd> g
***NDIS*** (c, 56) ==>ndisMRequest
***NDIS*** (c, 89) ndisMRequest: Queueing request 0xfc8fbc68
***NDIS*** (c, 392) ==>ndisMDoRequests
***NDIS*** (c, 419) ndisMDoRequests: Processing Request 0xfc8fbc68, Oid 0xff00c913
***NDIS*** (c, 2473) ==>ndisMSyncQueryInformationComplete
***NDIS*** (c, 2503) ndisMSyncQueryInformaitonComplete: Request 0xfc8fbc68, Oid 0xff00c913

The Dbglevel and Dbgsystems settings are toggle settings. To turn off the debug print feature for the REQUEST and BIND code paths, you must re-enter the previous command, as shown here:

0: kd> !ndiskd.dbgsystems REQUEST BIND
Arg = REQUEST
Arg = BIND

0: kd> !ndiskd.dbgsystems
Current settings:
Available settings:
INIT CONFIG SEND RECV PROTOCOL
BIND BUS_QUERY REGISTRY MEMORY FILTER
REQUEST WORK_ITEM PNP PM OPEN
LOCKS RESET WMI NDIS_CO REFERENCE

The preceding example shows how you can set and reset the verbosity and component values, which control the level of debugging information that NDIS prints in the debugger.

NDIS Bug Checks

The system performs a bug check when it detects an error caused by an NDIS miniport driver. The bug check is performed as soon as an error is detected. If the system did not stop, these errors would appear later, making it difficult to pinpoint the cause of the problem at that stage.

The bug checks that are caused by an NDIS miniport driver use a special bug check code. On Windows XP and Windows 2000, the bug check code is BUGCODE_ID_DRIVER. On Windows Server 2003, the bug check code is BUGCODE_NDIS_DRIVER. If a debugger is connected to the system being tested, a sentence describing the bug check is printed in the debugger. The !analyze –v command provides detailed information on the bug check.

Examples of NDIS miniport driver problems that cause the system to perform a bug check include the following:

  • Before the ownership of a receive packet, that the miniport driver indicated by calling the NdisMIndicateReceivePacket function, is transferred back to the miniport driver, the driver indicates the packet in another call to NdisMIndicateReceivePacket.
  • The miniport driver frees shared memory that it has not allocated.
  • The miniport driver unloads without deregistering an interrupt that it previously registered.

Note: For more information about bug check codes, see the Debugger.chm help file in the Microsoft Debugging Tools package.

PnP Overview

NDIS implements the following Plug and Play (PnP) functionality on behalf of the miniport:

  • NDIS intercepts all the PnP driver and device IRPs that are sent to the miniport driver.
  • NDIS and the driver together comprise the functional device object (FDO) of the miniport adapter.
  • NDIS implements the AddDevice routine on behalf of the minport driver. In response to the StartDevice IRP, NDIS calls the MiniportInitialize function handler of the miniport driver.
  • NDIS propagates the Remove/Stop IRP to the miniport driver’s MiniportHalt function. Before calling this function, NDIS unbinds all the protocols bound to that miniport.

The !ndiskd.miniport command prints information that is useful for debugging PnP problems, as shown in the following example.

0: kd> !miniport 80dc76f0
Miniport 80e16130 : Sample Ethernet Driver
AdapterContext : 80cd52c0
Flags         : 2c412048
BUS_MASTER, SG_LIST, IGNORE_TOKEN_RING_ERRORS
RESOURCES_AVAILABLE, SUPPORTS_MEDIA_SENSE,
DOES_NOT_DO_LOOPBACK, MEDIA_CONNECTED,
PnPFlags       : 00210000
RECEIVED_START, HARDWARE_DEVICE,
InternalResetCount   : 0000
MiniportResetCount   : 0000
References           : 3
UserModeOpenReferences: 0
PnPDeviceState       : PNP_DEVICE_STARTED
CurrentDevicePowerState : PowerDeviceD0
Bus PM capabilities
DeviceD1:       1
DeviceD2:       1
WakeFromD0:     0
WakeFromD1:     1
WakeFromD2:     1
WakeFromD3:     1
SystemState     DeviceState
PowerSystemUnspecified  PowerDeviceUnspecified
S0          D0
S1          PowerDeviceUnspecified
S2          PowerDeviceUnspecified
S3          D3
S4          D3
S5          D3
SystemWake: PowerSystemUnspecified
DeviceWake: PowerDeviceUnspecified
Current PnP and PM Settings:         : 00000030
DISABLE_WAKE_UP, DISABLE_WAKE_ON_RECONNECT,
Allocated Resources:
IO Port: 0000ec00, Length: 80
Memory: fe004000, Length: 80
Interrupt Level: 44, Vector: 44
Translated Allocated Resources:
     IO Port: 0000ec00, Length: 80
     Memory: fe004000, Length: 80
     Interrupt Level: 9, Vector: 1a3
MediaType     : 802.3
DeviceObject   : 80e16030, PhysDO : 80e939a0 Next DO: 80e939a0
MapRegisters   : 00000000
FirstPendingPkt: 00000000
DriverVerifyFlags : 00000000
Miniport Interrupt : 80cd5530
Miniport Open Block Queue:
     ffb31d18: Protocol ffb57cf8 = NDISUIO, ProtocolBindingContext ffb323e8
     ffbad958: Protocol 80e15998 = TCPIP, ProtocolBindingContext ffbadc00

The !miniport extension prints a lot of useful information. The items that the previous example prints include the following:

  • The PnP state of the miniport adapter.
  • The physical device object (PDO) and the FDO of the miniport adapter.
  • The protocols that are bound to the miniport adapter.
  • The hardware resources that are assigned to the miniport adapter.

Debugging a Miniport Initialization Problem

Miniport adapters can fail to initialize for a variety of reasons. If the problem is reproducible, the best way to proceed is to turn on NDIS tracing and reproduce the problem. The debug messages printed in the debugger makes it easy to identify the point of failure.

To debug a miniport initialization problem:

  1. Turn on NDIS tracing.
  2. Look for the statement specifying the failure code that the MiniportInitialize function handler returns.
  3. Using the failure code as a starting point, find the last NDIS function that the miniport called before it entered the failure code path.
  4. Inspect the source code of the miniport driver to find the cause of the fault.

The following example contains the debugging messages that are printed during the initialization of a miniport adapter.

<==ndisAddDevice: Miniport 81AF6338
ndisPnPAddDevice returning 0
==>ndisPnPStartDevice: DeviceObject
ndisPnPStartDevice: Miniport 81AF6338, Sample Ethernet Driver
…
==NdisMQueryAdapterResources: Miniport 81AF6338, Status 0
==>NdisMRegisterIoPortRange: Miniport 81AF6338
==>ndisTranslateResources: Miniport 81AF6338
translating resource   type: 1, value: 1800
ndisTranslateResources translated 1800 to 1800
<==ndisTranslateResources: Miniport 81AF6338, Status 0
<==NdisMRegisterIoPortRange: Miniport 81AF6338, Status 0
==>NdisWriteErrorLogEntry
<==NdisWriteErrorLogEntry
==>NdisMDeregisterIoPortRange: Miniport 81AF6338
<==NdisMDeregisterIoPortRange: Miniport 81AF6338
ndisMInitializeAdapter: Miniport 81AF6338, InitializeHandler returned c0000001
==>NdisMDeregisterAdapterShutdownHandler: Miniport 81AF6338
<==NdisMDeregisterAdapterShutdownHandler: Miniport 81AF6338
***NDIS*** (b, 1791)     INIT FAILURE: Dequeueing the miniport from the driver block.
==>ndisDeQueueMiniportOnDriver, Miniport 81AF6338, MiniBlock 81BB7588
<==ndisDeQueueMiniportOnDriver: Miniport 81AF6338, MiniBlock 81BB7588
***NDIS*** (b, 1822)     INIT FAILURE: Dereferencing the miniport block.

The preceding example shows a miniport adapter failing its initialization routine. In the beginning of the example, NDIS gets called at its AddDevice routine for the miniport, followed by a StartDevice IRP. As part of the IRP, NDIS calls the MiniportInitialize function handler, which fails because of an error that occurred after it successfully called the NdisMRegisterIoPortRange function. You can use this information as the starting point for debugging the initialization problem. NDIS debug messages also tell you if the NDIS API failed and possibly why it failed.

Debugging the Failure of a Miniport to Halt

If NDIS does not call a miniport driver’s MiniportHalt function when the miniport adapter is disabled through the user interface or an automated test, it is usually because an operation that the miniport driver must complete before being halted has not completed. For example, NDIS will not halt a miniport adapter until the driver finishes sending all the packets that were sent to it for transmission.

To debug the failure of a miniport to halt:

  1. Run the !stacks 2 ndis! command in the debugger to print all the threads that contain the “ndis!” string in their stacks.
  2. Inspect the threads to see if they offer any clues. For example, a thread that shows TCP/IP waiting implies that the protocol is waiting for the miniport driver to complete send operations.
  3. To find the number of outstanding requests to send packets in the miniport, print all of the Mopen structureson the miniport as explained earlier in this paper. If the miniport driver is not completing packets back to the protocol, the Referencesvalue never returns to one. Calling the NdisCloseAdapter function also decreases this value by one.
  4. Investigate the miniport driver to see why the miniport is not completing the packets. The miniport driver calls the NdisMSendComplete function to complete such send operations.

The following example shows an MOpen structure between the NDISUIO protocol and the Sample Ethernet Driver miniport.

0: kd> !mopen ffaefd70
Miniport Open Block ffaefd70
Protocol ffae6ad8 = NDISUIO, ProtocolContext ffaeb2e8
Miniport 80d74698 = Sample Ethernet Driver
MiniportAdapterContext: 80ccb008
Flags                 : 00218010
OPEN_PROCESSING, OPEN_CLOSING, OPEN_UNBINDING
OPEN_DONT_FREE,
References           : 43

The preceding example shows an Mopen structure between the NDISUIO protocol and the Sample Ethernet Driver. NDIS has called the protocol’s ProtocolUnbindAdapter handler.The protocol has called the NdisCloseAdapter function. NDIS will not call the protocol’s CloseAdapterComplete handler until the number of references reaches zero. The references are caused by outstanding send commands in the miniport.

Debugging Power Management Issues

As the device power policy owner of the miniport, NDIS is responsible for querying the bus driver, the miniport driver, and the system settings before deciding on the power management policy for the miniport adapter.

To find the values returned by the bus driver and the miniport driver, turn on NDIS tracing or look at the output of the !miniport command:

0: kd> !miniport 80dc76f0
Miniport 80e16130 : Sample Ethernet Driver
AdapterContext : 80cd52c0
Flags         : 2c412048
BUS_MASTER, SG_LIST, IGNORE_TOKEN_RING_ERRORS
RESOURCES_AVAILABLE, SUPPORTS_MEDIA_SENSE,
DOES_NOT_DO_LOOPBACK, MEDIA_CONNECTED,
PnPFlags       : 00210000
RECEIVED_START, HARDWARE_DEVICE,
InternalResetCount   : 0000
MiniportResetCount   : 0000
References           : 3
UserModeOpenReferences: 0
PnPDeviceState       : PNP_DEVICE_STARTED
   CurrentDevicePowerState : PowerDeviceD0
   Bus PM capabilities
    DeviceD1:       1
    DeviceD2:       1
    WakeFromD0:     0
    WakeFromD1:     1
    WakeFromD2:     1
    WakeFromD3:     1
    SystemState     DeviceState
    PowerSystemUnspecified  PowerDeviceUnspecified
    S0          D0
    S1          PowerDeviceUnspecified
    S2          PowerDeviceUnspecified
    S3          D3
    S4          D3
    S5          D3
    SystemWake: PowerSystemUnspecified
    DeviceWake: PowerDeviceUnspecified
   Current PnP and PM Settings:         : 00000030
                    DISABLE_WAKE_UP, DISABLE_WAKE_ON_RECONNECT,
Allocated Resources:
IO Port: 0000ec00, Length: 80
Memory: fe004000, Length: 80
Interrupt Level: 44, Vector: 44
Translated Allocated Resources:
IO Port: 0000ec00, Length: 80
Memory: fe004000, Length: 80
Interrupt Level: 9, Vector: 1a3
MediaType     : 802.3
DeviceObject   : 80e16030, PhysDO : 80e939a0 Next DO: 80e939a0
MapRegisters   : 00000000
FirstPendingPkt: 00000000
DriverVerifyFlags : 00000000
Miniport Interrupt : 80cd5530
Miniport Open Block Queue:
ffb31d18: Protocol ffb57cf8 = NDISUIO, ProtocolBindingContext ffb323e8
ffbad958: Protocol 80e15998 = TCPIP, ProtocolBindingContext ffbadc00

In the preceding example, NDIS sets the DISABLE_WAKE_UP flag, because the user has not enabled Wake-On-LAN (WOL) for this miniport adapter. To debug a WOL problem, you need to inspect the power management values returned by the system, the bus driver, and the miniport to determine which of the three entities is disabling WOL. These values are all displayed in the output of the !miniport command.

Debugging Interrupt Storms

Interrupt storms occur when either a miniport driver fails to disable device interrupts or the device continues to assert an interrupt after that interrupt has been disabled. In either case, the computer is not usable until the device stops asserting interrupts.

To diagnose an interrupt storm problem:

  1. Set a breakpoint on the MiniportISR function.
  2. If MiniportISR returns TRUE, set a breakpoint on the MiniportDpc function. MiniportISR should not return TRUE until the MiniportDpc functionenables interrupts.
  3. Investigate why the device is asserting an interrupt before the DPC has completed.

MiniportReset Overview

When a miniport driver calls the NdisMResetComplete function, NDIS completes all outstanding requests on behalf of the driver. If that driver is a serialized driver, NDIS also returns all the send packets that NDIS has queued for the driver to the appropriate protocol drivers.

Best Practices for Developing Miniport Drivers

The following practices are followed by developers at Microsoft Corporation when developing NDIS drivers:

  • Running tools such as NDISTest and Driver Verifier.
  • Using debug messages.
  • Tracking Spin Locks.

Tools – Using NDISTest and Driver Verifier

NDISTest and Verifier.exe are good tools to use when developing NDIS drivers. NDISTest aids and verifies the completeness of a miniport driver. You should use it on both miniports and IM drivers. NDISTest cannot test protocols. Use Driver Verifier to ensure that common errors are easily found and rectified early in the development cycle.

Use Driver Verifier from the beginning of the development effort. Use NDISTest to test the functional completeness and robustness of the driver. You can run an individual test from the test suite to test specific features of the NDIS driver.

Using Debug Messages

The __LINE__ compiler directive is a useful tool. The compiler replaces each __LINE__ directive with the actual line number in the file, which is useful for logging or printing debugging messages.

Tracking Spin Locks

You can enhance a deserialized driver to track the acquisition and release of spin locks by recording each file and line number that acquired a spin lock. This record is cleared just before releasing the spin lock.

At the beginning of each .c file, include a #define statement that uniquely identifies the file, as shown in the following code. Be sure that the defined value is larger than the number of lines that a file could contain, because the line number and the file number will be combined into a single unsigned integer.

#define DRIVER_FILE     0x0010000

Define the following structures and macros in a header file of the driver:

typedef struct _SAMPLE_DRIVER_LOCK{
NDIS_SPIN_LOCK SpinLock;
ULONG           Location;
} SAMPLE_DRIVER_LOCK, *PSAMPLE_DRIVER_LOCK;

#define MiniportAcquireLock(_Lock)           \
NdisAcquireSpinLock (&((_Lock)->NdisSpinLock));\
(_Lock)->Location = (DRIVER_FILE + __LINE__);

#define MiniportReleaseSpinLock(_Lock)      \
(_Lock)->Location = 0;\
NdisReleaseSpinLock(&((_Lock)->NdisSpinLock));  \

The preceding structure and two macros enable you to quickly find the line of code that last acquired the spin lock. To verify that no spin-lock issues exist in the code, run Driver Verifier with the Deadlock Detection option turned on. Each acquisition of a spin lock will be tested to make sure that it occurred in the right order with respect to all the other spin locks in your driver.

Best Practices for Developing Intermediate Drivers

An NDIS IM driver is complex, because any of the entry points in the driver’s protocol module or miniport module can be called regardless of the state of the other module.

The IM driver must ensure that a notification, such as a send or receive command to one of its modules, does not result in an incorrect action from its other module. For example, when NDIS requests that an IM driver’s miniport module should send a packet, the driver must then determine whether the protocol module can propagate the packet down or if the packet needs to fail.

You should maintain a reference count on all outstanding actions that the IM driver will be called back for—actions such as send commands and requests that the driver initiates to the miniport below it. This reference count should be synchronized with the unbind code in the protocol. The IM miniport should also maintain a reference count of the number of outstanding receive commands that the IM driver has indicated. This count must be synchronized with the PnP code path in the IM miniport driver, to ensure that no receive commands occur after the IM miniport adapter has been halted.

Common Intermediate Driver Issues

When developing an IM driver, you might encounter the problem of TCP/IP not sending packets to the IM driver for transmission. Common causes of this problem include the following:

  • The IM driver is reporting an invalid 802.3 physical (MAC) address.
  • The IM driver is indicating corrupted packets to TCP/IP.
  • The IM driver’s MiniportTransferData function handler is returning corrupted packets.

You should run NDISTest on the IM driver to solve such problems.

Another problem is that outstanding send and request commands in the IM miniport module can prevent NDIS from calling an IM driver’s MiniportHalt routine after the driver has called the NdisIMDeInitializeDeviceInstance function. In this situation, the IM driver could have passed such a packet to the miniport adapter below it, or queued the packet internally. The Mopen structure can determine whether any packets were passed to the underlying miniport.

Best Practices for Developing Protocol Drivers

The problems that commonly occur with protocol drivers are similar to those that occur with IM drivers. As described earlier, it is important to synchronize the protocol driver’s PnP code path with the driver’s send code path.

Use the appropriate I/O Control (IOCTL) codes when designing the protocol driver’s IOCTL interface. IOCTLs should specify the METHOD_BUFFERED option as documented in the DDK and have the appropriate read or write privilege set in the IOCTL code. If an IOCTL is going be used only when an Administrator is logged on, the protocol driver should create a device object by calling the IoCreateDeviceSecure routine.

Call to Action and Resources

Call to Action:

  • For driver developers:
  • Run NDISTest and Verifier on NDIS drivers during development. These tools verify functional completeness and find common errors in a reproducible manner.
  • Build kernel debugger extensions to quickly extract useful information from the driver’s data structures.
  • Build diagnostic information into your NDIS driver to quickly identify issues.
  • Post questions on the public Microsoft device driver newsgroup. It is an excellent forum for both novice and expert developers.

For questions about debugging and developing NDIS drivers, post questions on the public Microsoft device driver newsgroup.

For specific questions about this paper, send e-mail to ndis6fb@microsoft.com.

Resources:

Microsoft Hardware and Driver Developer Information

http://www.microsoft.com/hwdev/

Microsoft Windows Driver Development Kit (DDK)

http://www.microsoft.com/ddk/

Microsoft Debugging Tools

http://www.microsoft.com/ddk/debugging/default.asp

Microsoft’s public device driver newsgroup

microsoft.public.development.device.drivers

发布者