DriverObject と DriverEntry


こんにちは、K 里です。今回は、WDM ドライバの基本部分について、WDK サンプルドライバの Toaster を用いて説明しようと思います。基本とはいえその範囲は広いので、まずは OS とドライバの関わりとオブジェクトベースとなる Driver Object、ドライバの初期化についてまとめました。今後は Tips 記事と交互に WDK のコア コンポーネントについて説明していきたいと考えています。お役にたてれば幸いです。


 


I/O Manager


Windows 上で動作するデバイス ドライバは、I/O Manager を始めとした Kernel (ntoskrnl.exe) 内の各種コンポーネントと密接な関係を持つことになります。I/O Manager は、I/O 操作を実現するための IRP (I/O Request Packet) というパケット駆動型のモデルを実装しています。あるドライバに対して I/O 操作が必要な場合、I/O Manager はこの IRP を介してドライバの制御を行います。ドライバは、I/O Manager から受け取った IRP を基にデバイスを制御することになります。以下は、ユーザーモードのアプリケーションが CreateFile 関数を呼び出した際の I/O 要求の流れを図解しています。


 io_req_flow


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


1. User mode Application は、Win32 ファイル名 (: C:\Windows\xxx) を使用して CreateFile を呼び出す


2. ntdll.dll (NT Layer DLL) は、Win32 ファイル名を NT (: \??\C:\Windows\xxx) に変換する


3. kernel32.dll (Windows NT Base API Client DLL) は、NT 名を使用して NtCreateFile を呼び出す


4. I/O Manager は、リクエストを再構成し、Object Manager に処理を渡す


5. Object Manager は、シンボリックリンクを解決し、トラバース (走査) チェックを実施する


6. Object Manager は、Device Object の処理のために I/O Manager に処理を戻す


7. I/O Manager は、Device Object のセキュリティをチェック (ACL など) する


8. I/O Manager は、ハンドルを作成し、IRP_MJ_CREATE をデバイス ドライバに送信する


9. デバイス ドライバは、必要に応じて追加のチェックを実施する


 


Kernel-Mode Managers and Libraries


http://msdn.microsoft.com/en-us/library/cc264615.aspx


 


WDM Driver の種類


WDM ドライバは、PnP (プラグアンドプレイ) 、電源管理、WMI をサポートする Windows ドライバモデルです。WDM では、特定のデバイスの全ての制御をドライバ単体で賄うことはできません。WDM ドライバには以下の 3 タイプのドライバがあり、デバイスに対して必ずバスドライバとファンクション ドライバとセットで制御することになります。フィルタドライバはオプションとして存在しています。


Ø  バス ドライバ – 特定の論理、物理バスを管理します。PCMCIAPCIUSBIEEE1394ISA などが対象です。後ほど出てくる WDK Sample Toaster 論理バス (BusEnum.sys) もバス ドライバの位置づけとなります。バスドライバは、自身が管理するバス上に接続されるデバイスを検知し、PnP Manager (Kernel 内のコンポーネント) へ通知します。また、バスの電力供給状態も管理します。


Ø  ファンクション ドライバ – 特定のデバイスを管理します。ファンクション ドライバは、I/O Manager に対してデバイスを制御するためのインターフェースを提供します。一般的に ”デバイス ドライバ” と呼ばれるものです。


Ø  フィルタ ドライバ – 前述したドライバの上位、もしくは下位に位置づけられるドライバで、対象ドライバの補完ないし変更を行うドライバです。


 


Types of WDM Drivers


http://msdn.microsoft.com/en-us/library/aa490241.aspx


 


Driver の構成


I/O Manager と併せて、ハードウェア動作を管理する (抜き差しの検出、リソース割り当ての調整など) PnP (Plug and Play) Manager、デバイス単位で電力消費を管理する Power Manager、実装と診断性のための情報提供を行う WMI (Windows Management Instrumentation) といったコンポーネントを包括して I/O System と呼びます。以下は、I/O System と関係するドライバの主要な処理となります。


 


io_sys


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


 


Initialize Routine は、ドライバがロードされるタイミングで呼び出される初期化処理です。一般的に DriverEntry という関数名になります (ANSI C プログラムで言う main 関数に該当します)Add Device Routine は、PnP サポート対象のドライバで実装されます。PnP Manager は、この処理を介してデバイスがシステムに追加されたことを知り得ることができます。Dispatch Routine は、ドライバの各制御関数となります。例えば、Create/Close/Read/Write といった基本的な制御関数です。これらは、I/O Manager からの IRP を基に適切な処理が呼び出されることになります。後の Start I/OISRDPC については、別記事で説明する予定ですので今回は割愛します。


 


上記の処理 (関数) をドライバ側で実装し、それらの呼び出しタイミングを I/O Manager に委ねることになります。


 


Introduction to Standard Driver Routines


http://msdn.microsoft.com/en-us/library/ms794996.aspx


Standard Driver Routine Requirements


http://msdn.microsoft.com/en-us/library/ms794972.aspx


 


Driver Object


I/O Manager は、インストール&ロードされたシステム内の個々のドライバを表現するために、Driver Object を作成します。I/O Manager は、各ドライバが実装する初期化ルーチン (DriverEntry) を呼び出す際に Driver Object を渡します。ドライバは、その Driver Object に各 Dispatch Routine のエントリポイントを登録します。I/O Manager は、Driver Object に登録された各 Routine を、必要に応じて呼び出すことになります。なお Driver Object は、DRIVER_OBJECT 構造体として定義されます。


 


Introduction to Driver Objects


http://msdn.microsoft.com/en-us/library/ms795005.aspx


 


それでは、WDK sample プログラムの Toaster を使用して Driver Object を確認してみましょう。ここでは、Toaster バスドライバ (BusEnum.sys) を使用します。BusEnum.sys のインストールは以下のとおりです。デバイスマネージャーで、[表示] -> [デバイス(接続別)] とし、Toaster Bus Enumerator が追加されていれば成功です。なお今回は先日リリースされました WDK 7.0.0 を使用しています。


 


Toaster


http://msdn.microsoft.com/en-us/library/dd163450.aspx


 



 







(1)   以下のフォルダをビルドします (ビルドについては、なおきお~さんの過去記事をご参照ください)


    %WinDDK%\7100.0.0\src\general\toaster\wdm\bus


(2)   対象 OS Windows 7 の場合には、toaster\devicemetadatapackage フォルダ内のメタデータファイルを ProgramData フォルダにコピーします。コピー先は以下になります。


%ProgramData%\Microsoft\Windows\DeviceMetadataStore


(3) BusEnum.sys toaster\wdm\inf フォルダ内の bus.inf を適当なフォルダを作成、コピーします。


(4) WDK 付属ツール Devcon を使用して BusEnum.sys をインストールします (*1)(*2)


    >devcon.exe install businf.inf root\busenum


    *1. DevCon については、以下をご参照ください。


   デバイス マネージャとして機能する DevCon コマンド ライン ユーティリティ


http://support.microsoft.com/kb/311272/ja


    *2. DevCon は、%WinDDK%\7100.0.0\tools\devcon 配下にあります。


    *3. [コントロール パネル] -> [ハードウェアの追加] からもドライバのインストールは可能です。


詳細については、toaster フォルダ内のヘルプファイル (htm) をご覧ください。


toaster


 


次に、上記で Toaster Bus Enumerator をインストールしたシステムに対して、カーネルデバッグを実施します (ホスト側で、BusEnum.sys のシンボルファイル (BusEnum.pdb) をロードすることをお忘れなく!)。デバッグ接続が完了したら、BusEnum.sys Driver Object を確認してみましょう。確認するためのコマンドは、!drvobj コマンド+ドライバ名、もしくは !object コマンド+シンボルリンク名などがあります。ただ、個々のドライバを確認するだけなら、!drvobj コマンドでオプション 7 を指定すると事足りると思います。!drvobj 7 で、アドレス、シンボルリンク名、Device Object のアドレス、各種 Dispatch Routine で登録されているエントリポイントが表示できます。


 







0: kd> !drvobj busenum 7


Driver object (88b33338) is for:


 \Driver\busenum


Driver Extension List: (id , addr)


 


Device Object list:


88a74368 


 


DriverEntry:   b1026531     busenum!GsDriverEntry


DriverStartIo: 00000000    


DriverUnload:  b10219a0     busenum!Bus_DriverUnload


AddDevice:     b1021a60     busenum!Bus_AddDevice


 


Dispatch routines:


[00] IRP_MJ_CREATE                      b1021610        busenum!Bus_CreateClose


[01] IRP_MJ_CREATE_NAMED_PIPE           804fb739        nt!IopInvalidDeviceRequest


[02] IRP_MJ_CLOSE                       b1021610        busenum!Bus_CreateClose


[03] IRP_MJ_READ                        804fb739        nt!IopInvalidDeviceRequest


[04] IRP_MJ_WRITE                       804fb739        nt!IopInvalidDeviceRequest


[05] IRP_MJ_QUERY_INFORMATION           804fb739        nt!IopInvalidDeviceRequest


[06] IRP_MJ_SET_INFORMATION             804fb739        nt!IopInvalidDeviceRequest


[07] IRP_MJ_QUERY_EA                    804fb739        nt!IopInvalidDeviceRequest


[08] IRP_MJ_SET_EA                      804fb739        nt!IopInvalidDeviceRequest


[09] IRP_MJ_FLUSH_BUFFERS               804fb739        nt!IopInvalidDeviceRequest


[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    804fb739        nt!IopInvalidDeviceRequest


[0b] IRP_MJ_SET_VOLUME_INFORMATION      804fb739        nt!IopInvalidDeviceRequest


[0c] IRP_MJ_DIRECTORY_CONTROL           804fb739        nt!IopInvalidDeviceRequest


[0d] IRP_MJ_FILE_SYSTEM_CONTROL         804fb739        nt!IopInvalidDeviceRequest


[0e] IRP_MJ_DEVICE_CONTROL              b1021790        busenum!Bus_IoCtl


[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     804fb739        nt!IopInvalidDeviceRequest


[10] IRP_MJ_SHUTDOWN                    804fb739        nt!IopInvalidDeviceRequest


[11] IRP_MJ_LOCK_CONTROL                804fb739        nt!IopInvalidDeviceRequest


[12] IRP_MJ_CLEANUP                     804fb739        nt!IopInvalidDeviceRequest


[13] IRP_MJ_CREATE_MAILSLOT             804fb739        nt!IopInvalidDeviceRequest


[14] IRP_MJ_QUERY_SECURITY              804fb739        nt!IopInvalidDeviceRequest


[15] IRP_MJ_SET_SECURITY                804fb739        nt!IopInvalidDeviceRequest


[16] IRP_MJ_POWER                       b101eb40        busenum!Bus_Power


[17] IRP_MJ_SYSTEM_CONTROL              b1024140        busenum!Bus_SystemControl


[18] IRP_MJ_DEVICE_CHANGE               804fb739        nt!IopInvalidDeviceRequest


[19] IRP_MJ_QUERY_QUOTA                 804fb739        nt!IopInvalidDeviceRequest


[1a] IRP_MJ_SET_QUOTA                   804fb739        nt!IopInvalidDeviceRequest


[1b] IRP_MJ_PNP                         b1021e00        busenum!Bus_PnP


 


上記から BusEnum.sys の主要な Routine (Initialize/Add Device/Dispatch/StartIo) を確認することができます。ここでは表示されない ISR !idt コマンドで、DPC !timer コマンドで確認することができます。また、Dispatch Routine の中で、nt!IopInvalidDeviceRequest となっている IRP についてはドライバ側での実装がない (初期化時に routine のエントリポイントを Driver Object に紐付けていない) ことになります。つまり、BusEnum.sys で対応している IRP は以下の 6 種類となります。


Ø  IRP_MJ_CREATE


Ø  IRP_MJ_CLOSE


Ø  IRP_MJ_DEVICE_CONTROL


Ø  IRP_MJ_POWER


Ø  IRP_MJ_SYSTEM_CONTROL


Ø  IRP_MJ_PNP


 


ちなみに !object コマンドでは以下のようになります。







0: kd> !object \driver\busenum


Object: 88b33338  Type: (89bcc040) Driver


    ObjectHeader: 88b33320 (old version)


    HandleCount: 0  PointerCount: 3


    Directory Object: e13f35f8  Name: busenum


 


また、DRIVER_OBJECT 構造体を確認する場合は以下のようになります。







0: kd> dt nt!_DRIVER_OBJECT 88b33338 


   +0x000 Type             : 4


   +0x002 Size             : 168


   +0x004 DeviceObject     : 0x88a74368 _DEVICE_OBJECT


   +0x008 Flags            : 0x12


   +0x00c DriverStart      : 0xb101e000


   +0x010 DriverSize       : 0x9b80


   +0x014 DriverSection    : 0x89055668


   +0x018 DriverExtension  : 0x88b333e0 _DRIVER_EXTENSION


   +0x01c DriverName       : _UNICODE_STRING "\Driver\busenum"


   +0x024 HardwareDatabase : 0x8069d210 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"


   +0x028 FastIoDispatch   : (null)


   +0x02c DriverInit       : 0xb1026531     long  busenum!GsDriverEntry+0


   +0x030 DriverStartIo    : (null)


   +0x034 DriverUnload     : 0xb10219a0     void  busenum!Bus_DriverUnload+0


   +0x038 MajorFunction    : [28] 0xb1021610     long  busenum!Bus_CreateClose+0


 


0: kd> dt 0x88b333e0 _DRIVER_EXTENSION


nt!_DRIVER_EXTENSION


   +0x000 DriverObject     : 0x88b33338 _DRIVER_OBJECT


   +0x004 AddDevice        : 0xb1021a60     long  busenum!Bus_AddDevice+0


   +0x008 Count            : 0


   +0x00c ServiceKeyName   : _UNICODE_STRING "busenum"


 


上記で赤字で示している構造体メンバが、基本的にドライバ側でアクセス可能なメンバとなります。ただ、全てのメンバに対してドライバ側で情報を埋める必要はありません。DriverExtension は、DRIVER_EXTENSION サブ構造体として定義されています。そのメンバとなる AddDevice に、Add Device Routine のエントリポイントを登録します。FastIoDispatch メンバは、ファイルシステムやネットワークドライバなどの特定のドライバのみが使用します。あとは、DriverStartIo メンバに Start I/ODriverUnload メンバにクリーンアップ関数 (WDM では基本 Object/Register の解放のみ) となります。MajoFunction メンバは、各 Dispatch Routine のエントリポイントを登録するための配列となります。前述したドライバの主要な処理の中で、実装している処理を登録することになります。これらのデバッグ情報と併せて BusEnum.sys のソースコードを確認していただくと、より理解が深まると思います。


 







/** Busenum.c **/


 


NTSTATUS


DriverEntry (


    __in  PDRIVER_OBJECT  DriverObject,


    __in  PUNICODE_STRING RegistryPath


    )


  :


  :


    //


    // Set entry points into the driver


    //


    DriverObject->MajorFunction [IRP_MJ_CREATE] =


    DriverObject->MajorFunction [IRP_MJ_CLOSE] = Bus_CreateClose;


    DriverObject->MajorFunction [IRP_MJ_PNP] = Bus_PnP;


    DriverObject->MajorFunction [IRP_MJ_POWER] = Bus_Power;


    DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = Bus_IoCtl;


    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = Bus_SystemControl;


    DriverObject->DriverUnload = Bus_DriverUnload;


    DriverObject->DriverExtension->AddDevice = Bus_AddDevice;


 


Writing a DriverEntry Routine


http://msdn.microsoft.com/en-us/library/ms794742.aspx


 


次回以降の WDM 関連記事では、今回ちょこちょこ出てきた Device Object について説明しようと思います。


ではまた。


 

Skip to main content