CHAPTER 9: I/O System 538 551 558 560 563 565 568 578 589 Viewing the Loaded Driver List Viewing the System Power Capabilities and Policy Looking at the \Device Directory Viewing Win32 Device Name to W2K Device Name Mappings Displaying Driver and Device Objects Looking at Driver Dispatch Routines Examining IRPs and the Thread IRP Queue Dumping the Device Tree Looking at a Driver's Registered Fast I/O Routines EXPERIMENT Viewing the Loaded Driver List You can see a list of registered drivers by going to the Drivers section of the Computer Management Microsoft Management Console (MMC) snap-in or by right-clicking the My Computer icon on the desktop and selecting Manage from the context menu. (The Computer Management snap-in is in the Programs/Administrative Tools folder of the Start menu.) You can view the Drivers section within Computer Management by expanding System Tools, System Information, Software Environment and selecting Drivers, as shown here: You can also obtain a list of loaded kernel-mode drivers with the Drivers utility in the Windows 2000 resource kits or the Pstat utility (ships in the Platform SDK and is available for download from the Windows 2000 Resource Kits Web site at www.microsoft.com/windows2000/library/resources/reskit.) Pstat lists the drivers at the end of its display. (It first lists all the processes and threads in the system.) The only difference in the output of the two utilities is that Pstat shows the load address of the driver in system address space. The following output is a partial display of the driver information from Pstat: C:\>pstat ModuleName Load Addr Code Data Paged LinkDate ----------------------------------------------------------------------ntoskrnl.exe 80400000 429184 96896 775360 Tue Dec 07 18:41:11 1999 hal.dll 80062000 25856 6016 16160 Tue Nov 02 20:14:22 1999 BOOTVID.DLL EE010000 5664 2464 0 Wed Nov 03 20:24:33 1999 ACPI.sys BFFD8000 92096 8960 43488 Wed Nov 10 20:06:04 1999 WMILIB.SYS EE1C8000 512 0 1152 Sat Sep 25 14:36:47 1999 pci.sys EDC00000 12704 1536 31264 Wed Oct 27 19:11:08 1999 isapnp.sys EDC10000 14368 832 22944 Sat Oct 02 16:00:35 1999 compbatt.sys EE014000 2496 0 2880 Fri Oct 22 18:32:49 1999 BATTC.SYS EE100000 800 0 2976 Sun Oct 10 19:45:37 1999 intelide.sys EE1C9000 1760 32 0 Thu Oct 28 19:20:03 1999 PCIIDEX.SYS EDE80000 4544 480 10944 Wed Oct 27 19:02:19 1999 pcmcia.sys BFFBD000 32800 8864 23680 Fri Oct 29 19:20:08 1999 ftdisk.sys BFFA0000 4640 32 95072 Mon Nov 22 14:36:23 1999 Diskperf.sys EE102000 1728 32 2016 Thu Sep 30 20:30:40 1999 dmio.sys BFF7E000 104672 15168 0 Tue Nov 30 14:47:49 1999 If you're looking at a crash dump (or live system) with the kernel debugger, you can get a similar display with the kernel debugger !drivers command. EXPERIMENT Viewing the System Power Capabilities and Policy You can view a computer's system power capabilities by using the !pocaps kernel debugger command. Here's the output of the command when run on an ACPI-compliant laptop running Windows 2000 Professional: kd> !pocaps PopCapabilities @ 0x8046adc0 Misc Supported Features: PwrButton SlpButton Lid S1 S3 S4 S5 HiberFile FullWake Processor Features: Thermal Throttle (MinThrottle = 03, Scale = 08) Disk Features: SpinDown Battery Features: BatteriesPresent Battery 0 - Capacity: 00000000 Granularity: 00000000 Battery 1 - Capacity: 00000000 Granularity: 00000000 Battery 2 - Capacity: 00000000 Granularity: 00000000 Wake Caps Ac OnLine Wake: Sx Soft Lid Wake: Sx RTC Wake: S3 Min Device Wake: Sx Default Wake: Sx The Misc Supported Features line reports that, in addition to S0 (fully on), the system supports system power states S1, S3, S4, and S5 (it doesn't implement S2) and has a valid hibernation file to which it can save system memory when it hibernates (state S4). The Power Options Properties dialog box, shown below (available by selecting Power Options in Control Panel), lets you configure various aspects of the system's power policy. The exact properties you can configure depends on the system's power capabilities, which we just examined. Windows 2000 Professional on an ACPI-compliant laptop (such as the system on which we captured the following screen shot) generally provides the most power-management features. On such systems, you can set the idle detection timeouts that control when the system turns off the monitor, spins down hard disks, goes to standby mode (moves to system power state S1), and hibernates (moves the system to power state S4). In addition, the Advanced tab in Power Options lets you specify the power-related behavior of the system when you press the power or sleep buttons or close a laptop's lid. The settings you configure in Power Options directly affect values in the system's power policy, which you can display with the !popolicy debugger command. Here's the output of the command on the same system: kd> !popolicy SYSTEM_POWER_POLICY (R.1) @ 0x80469180 PowerButton: Off Flags: 00000003 SleepButton: Sleep Flags: 00000003 LidClose: Hibernate Flags: 00000001 Idle: None Flags: 00000001 OverThrottled: Sleep Flags: c0000004 NoWakes Critical IdleTimeout: 00000000 IdleSensitivity: MinSleep: S1 MaxSleep: LidOpenWake: S0 FastSleep: WinLogonFlags: 00000000 S4Timeout: VideoTimeout: 00000000 VideoDim: SpinTimeout: 00000708 OptForPower: FanTolerance: 64 ForcedThrottle: MinThrottle: 19 Event: Event: Event: Event: Event: 00000000 00000000 00000000 00000000 00000000 32 S3 S1 00000000 6e 01 64 Query UI Query UI Query Query Override The first lines of the display correspond to the button behaviors specified on the Advanced tab of Power Options, and on this system the power button is interpreted as an off switch, the sleep button moves the system to a sleep state, and the closing of the laptop lid causes the system to hibernate. The timeout values shown at the end of the output are expressed in seconds and displayed in hexadecimal notation. The values reported here directly correspond to the settings you can see configured in the Power Options screen shot. (The laptop is plugged in.) For example, the video timeout is 0, meaning the monitor never turns off, and the hard disk spin-down timeout is 0x708, which corresponds to 1800 seconds, or 30 minutes. EXPERIMENT Looking at the \Device Directory You can use the Winobj tool, included as \Sysint\Winobj.exe on the companion CD, or the !object kernel debugger command to view the device names under \Device in the object manager namespace. The following screen shot shows an I/O manager-assigned symbolic link that points to a device object in \Device with an autogenerated name: When you run the !object kernel debugger command and specify the \Device directory, you should see output similar to the following: kd> !object \device Object: 81a8e170 Type: (81ab6120) Directory ObjectHeader: 81a8e158 HandleCount: 0 PointerCount: 198 Directory Object: 81a914d0 Name: Device 9 symbolic links snapped through this directory HashBucket[ 00 ]: 81a6de10 Device 'KsecDD' 819c43f0 Device 'Beep' 81a6d6d0 Device 'Ndis' 81a61030 Device '00000019' 81aa7830 Device '003018' 81aa7a30 Device '002918' HashBucket[ 01 ]: 817c3c70 Device '00000026' 819c3d90 Device 'Netbios' HashBucket[ 02 ]: 818d1850 Device 'KSENUM#00000001' 81966890 Device 'Ip' HashBucket[ 03 ]: 818c5b70 Device 'KSENUM#00000002' 81a31038 Device 'Video0' 81a4fc70 Device 'KeyboardClass0' HashBucket[ 04 ]: 819d4410 Device 'NDProxy' 819c0040 Device 'Video1' HashBucket[ 05 ]: 817e7650 Device 'PcCard0-0' 819c70d0 Device 'Eawdmfd' 81a2aa50 Device '{13199AF4-86FA-48C2-8074-468CA06AFB6C}' 819c0ce0 Device 'Video2' 81a52040 Device 'Serial0' 81a6cbb0 Device 'PointerClass0' 81a215f0 Device '0000000a' HashBucket[ 06 ]: 817cb8c0 Device 'Serial1' 81900570 Device 'DebugMessageDevice' 81a277d0 Device 'USBPDO-0' 81a5e030 Device 'CompositeBattery' When you execute !object and specify an object manager directory object, the kernel debugger dumps the contents of the directory according to the way the object manager organizes it internally. For fast lookups, a directory stores objects in a hash table based on a hash of the object names, so the output shows the objects stored in each bucket of the directory's hash table. EXPERIMENT Viewing Win32 Device Name to Windows 2000 Device Name Mappings You can examine the symbolic links that define the Win32 device namespace with the Winobj utility included on the companion CD (\Sysint\Winobj.exe). Run Winobj, and click on the \?? directory, as shown here: Notice the symbolic links on the right. Try double-clicking on the device C:. You should see something like this: C: is a symbolic link to the internal device named \Device\HarddiskVolume1, or the first volume on the first hard drive in the system. The COM1 entry in Winobj is a symbolic link to \Device\Serial0, and so forth. Try creating your own links with the subst command at a command prompt. EXPERIMENT Displaying Driver and Device Objects You can display driver and device objects with the kernel debugger !drvobj and !devobj commands, respectively. In the following example, the driver object for the keyboard class driver is examined, and its lone device object viewed. kd> !drvobj kbdclass Driver object (81869cb0) is for: \Driver\Kbdclass Driver Extension List: (id , addr) Device Object list: 81869310 kd> !devobj 81869310 Device object (81869310) is for: KeyboardClass0 \Driver\Kbdclass DriverObject 81869cb0 Current Irp a57a0e90 RefCount 0 Type 0000000b Flags 00002044 DevExt 818693c8 DevObjExt 818694b8 ExtensionFlags (0000000000) AttachedDevice (Upper) 818691e0 \Driver\Ctrl2cap AttachedTo (Lower) 81869500 \Driver\i8042prt Device queue is busy -- Queue empty. Notice that the !devobj command also shows you the addresses and names of any device objects that the object you're viewing is layered over (the AttachedTo line) as well as the device objects layered on top of the object specified (the AttachedDevice line). EXPERIMENT Looking at Driver Dispatch Routines You can obtain a listing of the functions a driver has defined for its dispatch routines by entering a 2 after the driver object's name (or address) in the !drvobj kernel debugger command: kd> !drvobj kbdclass 2 Driver object (81869cb0) is for: \Driver\Kbdclass Dispatch routines: [00] IRP_MJ_CREATE kbdclass!KeyboardClassCreate [01] IRP_MJ_CREATE_NAMED_PIPE ntoskrnl!IopInvalidDeviceRequest [02] IRP_MJ_CLOSE kbdclass!KeyboardClassClose [03] IRP_MJ_READ kbdclass!KeyboardClassRead [04] IRP_MJ_WRITE ntoskrnl!IopInvalidDeviceRequest [05] IRP_MJ_QUERY_INFORMATION ntoskrnl!IopInvalidDeviceRequest [06] IRP_MJ_SET_INFORMATION ntoskrnl!IopInvalidDeviceRequest [07] IRP_MJ_QUERY_EA ntoskrnl!IopInvalidDeviceRequest [08] IRP_MJ_SET_EA ntoskrnl!IopInvalidDeviceRequest edf0866e 80425354 edf088ec edf08b1c 80425354 80425354 80425354 80425354 80425354 [09] IRP_MJ_FLUSH_BUFFERS edf085d4 kbdclass!KeyboardClassFlush [0a] IRP_MJ_QUERY_VOLUME_INFORMATION 80425354 ntoskrnl!IopInvalidDeviceRequest [0b] IRP_MJ_SET_VOLUME_INFORMATION 80425354 ntoskrnl!IopInvalidDeviceRequest [0c] IRP_MJ_DIRECTORY_CONTROL 80425354 ntoskrnl!IopInvalidDeviceRequest [0d] IRP_MJ_FILE_SYSTEM_CONTROL 80425354 ntoskrnl!IopInvalidDeviceRequest [0e] IRP_MJ_DEVICE_CONTROL edf0a8ec kbdclass!KeyboardClassDeviceControl [0f] IRP_MJ_INTERNAL_DEVICE_CONTROL edf0a380 kbdclass!KeyboardClassPassThrough [10] IRP_MJ_SHUTDOWN 80425354 ntoskrnl!IopInvalidDeviceRequest [11] IRP_MJ_LOCK_CONTROL 80425354 ntoskrnl!IopInvalidDeviceRequest [12] IRP_MJ_CLEANUP edf084b6 kbdclass!KeyboardClassCleanup [13] IRP_MJ_CREATE_MAILSLOT 80425354 ntoskrnl!IopInvalidDeviceRequest [14] IRP_MJ_QUERY_SECURITY 80425354 ntoskrnl!IopInvalidDeviceRequest [15] IRP_MJ_SET_SECURITY 80425354 ntoskrnl!IopInvalidDeviceRequest [16] IRP_MJ_POWER edf0b5e2 kbdclass!KeyboardClassPower [17] IRP_MJ_SYSTEM_CONTROL edf0bbfe kbdclass!KeyboardClassSystemControl [18] IRP_MJ_DEVICE_CHANGE 80425354 ntoskrnl!IopInvalidDeviceRequest [19] IRP_MJ_QUERY_QUOTA 80425354 ntoskrnl!IopInvalidDeviceRequest [1a] IRP_MJ_SET_QUOTA 80425354 ntoskrnl!IopInvalidDeviceRequest [1b] IRP_MJ_PNP edf09168 kbdclass!KeyboardPnP EXPERIMENT Examining IRPs and the Thread IRP Queue You can examine the pending IRPs for a thread with the !thread kernel debugger command. One thread that almost always has a pending IRP is the Win32 environment subsystem's keyboard-input thread. To find this thread, execute the !stacks kernel debugger command and locate the thread in Csrss that is listed as having started in the Win32k RawInputThread function: kd> !stacks Proc.Thread Thread ThreadState 8.000004 8.00000c 8.000010 8.000014 8.000018 8.00001c 8.000020 8.000024 fe504a60 fe503ce0 fe503a60 fe5037e0 fe503560 fe5032e0 fe502020 fe502da0 BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED c0.0000c4 c0.0000c8 c0.0000cc c0.0000d0 c0.0000d4 c0.0000dc c0.00007c c0.0000e0 ff2d5020 ff2d5d80 ff2d4820 ff2d4460 ff2d4120 ff2cfda0 ff2cbc40 ff2cb480 BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED BLOCKED Blocker [System] ntoskrnl!MmZeroPageThread+0x5f ntoskrnl!ExpWorkerThread+0x73 ntoskrnl!ExpWorkerThread+0x73 ntoskrnl!ExpWorkerThread+0x73 ntoskrnl!ExpWorkerThread+0x73 ntoskrnl!ExpWorkerThread+0x73 ntoskrnl!ExpWorkerThread+0x73 ntoskrnl!ExpWorkerThread+0x73 [csrss.exe] ?? Kernel stack not resident ?? ?? Kernel stack not resident ?? ntdll+0xaaa7 ?? Kernel stack not resident ?? ?? Kernel stack not resident ?? ntdll+0xaaa7 win32k!RawInputThread+0x3c2 win32k!xxxMsgWaitForMultipleObjects+0x92 Then perform the !thread command on the thread's address (the second column): kd> !thread ff2cbc40 THREAD ff2cbc40 Cid c0.7c Teb: 00000000 Win32Thread: a20836e8 WAIT: (WrUserRequest) KernelMode Alertable ff2cc1a0 SynchronizationEvent ff2cbb28 SynchronizationEvent ff2cbae8 NotificationTimer ff2cbb68 SynchronizationEvent IRP List: fee25388: (0006,0100) Flags: 00000970 Mdl: 00000000 Not impersonating Owning Process ff2d9020 WaitTime (seconds) 15420441 Context Switch Count 328592 UserTime 0:00:00.0000 KernelTime 0:00:00.0721 Start Address win32k!RawInputThread (0xa000c0b0) Stack Init f20d0000 Current f20cfaf0 Base f20d0000 Limit f20cd000 Call 0 Priority 19 BasePriority 13 PriorityDecrement 0 DecrementCount 0 ChildEBP RetAddr Args to Child f20cfb08 8042d33d 80400b46 00000001 00000000 ntoskrnl!KiSwapThread+0xc5 f20cfb3c a000c3f3 00000004 ff2cbc08 00000001 ntoskrnl!KeWaitForMultipleObjects+0x266 f20cfda8 804524f6 00000002 00000000 00000000 win32k!RawInputThread+0x3c2 f20cfddc 80465b62 a000c0b0 f1caf7d0 00000000 ntoskrnl!PspSystemThreadStartup+0x69 00000000 00000000 00000000 00000000 00000000 ntoskrnl!KiThreadStartup+0x16 The sample output shows that the thread's IRP list contains one pending IRP. If you use the !irp command on this IRP, you're likely to see something like this: kd> !irp fee25388 Irp is active with 4 stacks 4 is current (= 0xfee25440) No Mdl System buffer = ff0acc48 Thread ff2cbc40: Irp stack trace. cmd flg cl Device File Completion-Context [ 0, 0] 0 0 00000000 00000000 00000000-00000000 [ 0, 0] Args: 00000000 00000000 00000000 00000000 0 0 00000000 00000000 00000000-00000000 [ 0, 0] Args: 00000000 00000000 00000000 00000000 0 0 00000000 00000000 00000000-00000000 >[ 4, 0] Args: 00000000 00000000 00000000 00000000 0 e1 ff43b390 ff2cb928 00000000-00000000 \Driver\Kbdclass Args: 00000078 00000000 00000000 00000000 This output shows that the IRP has four stack locations and that the keyboard class driver, which is waiting for keyboard input before it completes the IRP, currently owns it. Another IRP-related debugger command, !irpfind, lets you see all the pending IRPs on the system: kd> !irpfind Scanning large pool allocation table for Tag: Irp? Searching NonPaged pool (fe314000 : fe52c000) for Tag: Irp? Irp [ Thread ] fe4f0568 [00000000] fe4f22e8 [fe5028a0] fe4f3b28 [fe5028a0] fe4fdf68 [ff2ae8c0] 0xff2c9780 fe50b6a8 [00000000] fe50d648 [00000000] fe513e68 [fe5028a0] fe515e68 [fe5028a0] irpStack: irpStack: irpStack: irpStack: irpStack: (Mj,Mn) ( 0, 0) ( e, 0) ( e, 0) ( e, 0) DevObj ff453790 fe4f13b0 fe4f33f0 ff3f45f0 [Driver] [ \Driver\Cdrom] [ \Driver\Ftdisk] [ \Driver\Ftdisk] [ \Driver\NetBT] Irp is complete (CurrentLocation 3 > StackCount 2) irpStack: ( f, 0) ff4526f0 [ \Driver\openhci] irpStack: ( e, 0) fe4f3690 [ \Driver\Ftdisk] irpStack: ( e, 0) fe4f2570 [ \Driver\Ftdisk] Searching NonPaged pool (fe52c000 : ffb7f000) for Tag: Irp? fefa4848 [ff1124e0] irpStack: ( e, 9) fefcc2e8 [ff0ecda0] irpStack: ( 3, 0) ff2b3490 [ \Driver\AFD] ff3f3e70 [ \FileSystem\Npfs] EXPERIMENT Dumping the Device Tree A more detailed way to view the device tree than using Device Manager is to use the !devnode kernel debugger command. Specifying 0 1 as command options dumps the internal device tree devnode structures, indenting entries to show their hierarchical relationships, as shown here: kd> !devnode 0 1 Dumping IopRootDeviceNode (= 0x818a78e8) DevNode 0x818a78e8 for PDO 0x818a79e0 Parent 0000000000 Sibling 0000000000 Child 0x818a74c8 InstancePath is "HTREE\ROOT\0" Flags (0x00040459) DNF_MADEUP, DNF_PROCESSED, DNF_ENUMERATED, DNF_ADDED, DNF_NO_RESOURCE_REQUIRED, DNF_STARTED DisableableDepends = 10 (from children) DevNode 0x818a74c8 for PDO 0x818a75d0 Parent 0x818a78e8 Sibling 0x818a7248 Child 0x81883228 InstancePath is "Root\ACPI_HAL\0000" Flags (0x000405dd) DNF_MADEUP, DNF_HAL_NODE, DNF_PROCESSED, DNF_ENUMERATED, DNF_ADDED, DNF_HAS_BOOT_CONFIG, DNF_BOOT_CONFIG_RESERVED, DNF_NO_RESOURCE_REQUIRED, DNF_STARTED DisableableDepends = 1 (from children) DevNode 0x81883228 for PDO 0x818838d0 Parent 0x818a74c8 Sibling 0000000000 Child 0x81819408 InstancePath is "ACPI_HAL\PNP0C08\0" ServiceName is "ACPI" Flags (0x000421d8) DNF_PROCESSED, DNF_ENUMERATED, DNF_ADDED, DNF_HAS_BOOT_CONFIG, DNF_BOOT_CONFIG_RESERVED, DNF_RESOURCE_ASSIGNED, DNF_STARTED CapabilityFlags (0x000000c0) UniqueID, SilentInstall DisableableDepends = 10 (from children) DevNode 0x81819408 for PDO 0x81891530 Parent 0x81883228 Sibling 0x818910c8 Child 0000000000 InstancePath is "ACPI\PNP0C0B\1" Flags (0x00040458) DNF_PROCESSED, DNF_ENUMERATED, DNF_ADDED, DNF_NO_RESOURCE_REQUIRED, DNF_STARTED UserFlags (0x00000008) DNUF_NOT_DISABLEABLE CapabilityFlags (0x000001c0) UniqueID, SilentInstall, RawDeviceOK DisableableDepends = 1 (including self) Information shown for each devnode includes the InstancePath, which is the name of the device's enumeration registry key stored under HKLM\SYSTEM\CurrentControlSet\Enum, and the ServiceName, which corresponds to the device's driver registry key under HKLM\SYSTEM\CurrentControlSet\Services. To see the resources, such as interrupts, ports, and memory assigned to each devnode, specify 0 3 as the command options for the !devnode command. EXPERIMENT Looking at a Driver's Registered Fast I/O Routines The !drvobj kernel debugger command can list the fast I/O routines that a driver registers in its driver object. However, typically only file system drivers have any use for fast I/O routines. The following output shows the fast I/O table for the NTFS file system driver object: kd> !drvobj \filesystem\ntfs 2 Driver object (ff432670) is for: \FileSystem\Ntfs Dispatch routines: [00] IRP_MJ_CREATE Fast I/O routines: FastIoCheckIfPossible be3263ef Ntfs!NtfsPostUsnChange+0xd8c FastIoRead be31869e Ntfs!NtfsCreateInternalStreamCommon+0x1b43 FastIoWrite be318df9 Ntfs!NtfsCreateInternalStreamCommon+0x229e FastIoQueryBasicInfo be3020fa Ntfs!NtfsRaiseStatus+0x105a8 FastIoQueryStandardInfo be317d1e Ntfs!NtfsCreateInternalStreamCommon+0x11c3 FastIoLock be32622d Ntfs!NtfsPostUsnChange+0xbca FastIoUnlockSingle be326139 Ntfs!NtfsPostUsnChange+0xad6 The output shows that NTFS has registered its NtfsPostUsnChange routine as the fast I/O table's FastIoCheckIfPossible entry. As the name of this fast I/O entry implies, the I/O manager sometimes calls this function before issuing a fast I/O request, giving a driver an opportunity to indicate when fast I/O operations on a file are not feasible.