none
WDK7.1(KMDF)でのZwMapViewOfSection関数に渡す長さに制限値があるのでしょうか。 RRS feed

  • 質問

  • はじめまして。

    Win7(x86)環境でWDK7.1 KMDFのデバイスドライバ作成方法の調査中です。
    その過程で遭遇した現象について知識がある方にご教授願いたくよろしくお願いします。
    ・あるハードから渡されるメモリ(BAR0)を、デバイスドライバとアプリケーションで
     アクセスするため、ZwMapViewOfSection関数を使用しています。
    ・メモリは、8MBのサイズなのですが、8MBをZwMapViewOfSection関数に渡すと
     ブルースクリーンになります。ZwMapViewOfSection関数の延長上のOS部分で異常終了
     しているようです。
    ・試しに512KBをZwMapViewOfSection関数に渡すと正常に動作します。
     制限値をさがしてはいないのですが、2MBは、異常終了。1MBは、異常終了。
     1MB+8は、正常。となりました。

     WDK7.1(KMDF)でのZwMapViewOfSection関数に渡す長さに制限値があるのでしょうか。
     また、回避方法は、あるのでしょうか。
     (たとえば、WdfDeviceInitSetxxxxxx関数で上限値を設定とか)

    よろしくお願いします。

    • 移動 Mike Wang (MSCS) 2012年10月2日 12:43 (移動元:Windows デバイスドライバー開発)
    2011年3月3日 4:17

回答

  • ご提示いただいたログをみると、BugCheck Code が 0x7A (KERNEL_DATA_INPAGE_ERROR)
    となっていますので、Page-in の処理で失敗しているようですが、一番の問題は IRQL だと思います。

    デバッグ ログに、以下の出力があります。

    CURRENT_IRQL:  2

    これは、BSOD を引き起こしたスレッドを処理しているときの IRQL が DISPATCH_LEVEL (2)
    であることを示していると思いますが、ZwMapViewOfSection() をコールしても良いのは、
    PASSIVE_LEVEL (0) のときだけです。
    この制限は、WDK Help Document や下記 MSDN サイトでも明記されており、また前回の返信で
    提示させていただいたサポート技術情報の解説でも触れられていると思います。

    ------------------------------------------------------------
    ZwMapViewOfSection

    http://msdn.microsoft.com/en-us/library/ff566481(VS.85).aspx
    ------------------------------------------------------------

    つまり今回ご投稿されている問題の場合、ZwMapViewOfSection() をコールする際に、
    512KB の場合は、IRQL == PASSIVE_LEVEL の状態であっても、たまたま運よく
    マップするサイズが小さかったので Page Fault を発生させる必要がなかっただけであり、
    8MB の場合はマップする View サイズとの関係上、必ず Page Fault を発生させる
    必要があったため、NT カーネルが明示的にクラッシュさせた。。。
    ということだと思います。
    もっと言えば、マップする View サイズが 512KB であっても、Pool が逼迫あるいは枯渇して
    いる場合には、BSOD になっていてもおかしくない。。。ということではないでしょうか?

    現在の IRQL は KeGetCurrentIrql() で取得することが可能なはずです。
    なので以下のような感じで、IRQL == PASSIVE_LEVEL の場合にのみ、
    ZwMapViewOfSection() をコールするように実装を変更すれば、とりあえずは
    これが問題の BSOD は発生しなくなると思います。
    (もっとも、先の返信でも触れましたが、そもそもこの関数は使わないほうがいい。。。
      と、個人的には思っていますけど。。。)

    ------------------------------------------------------------
    if ( KeGetCurrentIrql() == PASSIVE_LEVEL )
    {
        ....
        ZwMapViewOfSection(...);
        ....
    }
    else
    {
        // Error
    }
    ------------------------------------------------------------

    以上、参考になりましたら幸いです。

    • 回答としてマーク ryo_dev 2011年3月9日 1:12
    2011年3月7日 5:59
  • VRE様 ご回答ありがとうございます。

    試してみます。

    ありがとうございました。

     

    試してみました。

    WdfWorkItemCreate関数等でPassive_Levelにした中でZwMapViewOfSection関数を実行しました。

    8MBサイズでも成功しました。が、しかし、これで取得したアドレスをアプリに渡したのですが、

    ハードのメモリにマッピングされていないようです。

    残念ですが、別の実装を検討します。

    ありがとうございました。

     

    • 回答としてマーク ryo_dev 2011年3月9日 1:11
    2011年3月7日 6:41

すべての返信

  • 今回ご投稿されているご質問は、

    「ZwMapViewOfSection() でマップ出来るメモリ サイズに上限があるのか?」

    と言うことだと思いますが、ユーザアドレス空間にマップされるメモリの実態は
    Pool Memory に確保されると思いますので、当然現在利用可能な Pool Size 以上の
    メモリの確保は確保は不可能だと思います。


       各プラットフォーム毎の Pool Memory の最大値に関しては、下記サイトが参考に
       なると思います。

        ushing the Limits of Windows: Paged and Nonpaged Pool

        http://blogs.technet.com/b/markrussinovich/archive/2009/03/26/3211216.aspx

    そー言った意味では、ZwMapViewOfSection() でマップ出来るメモリ サイズには上限が
    あると思いますが、今回ご投稿されている内容を拝見する限り、それが原因ではないように
    思います。
    つまり、512KB で動いているのは「たまたま」であり、8MB の場合では「たまたま」で
    なくなっている。。。と言う気がしています。

    ZwMapViewOfSection() の使用方法に関しては、(既にご覧になっているかもしれませんが)
    下記サポート技術情報でも解説されており、その使用条件に関してはかなりの制限が
    あり、Microsoft もこの関数の使用に関しては推奨していないように見受けられます。


    ------------------------------------------------
    DDK: ユーザ モードとカーネル モード間のメモリ共有
    http://support.microsoft.com/kb/191840/ja
    ------------------------------------------------

    例えば、上記サイトでも言及していますが、この関数でマップしたユーザ アドレス空間は、
    当然マップを行ったプロセス コンテキストでのみ有効な訳で、他のプロセスからは一切
    アクセスすることができないと思います。
    これは、通常任意のプロセス コンテキストで動作することを前提とするドライバにとって、
    かなりの制約になると思います。
    (個人的には、止むを得ない理由がない限りこの関数を使うべきではないと考えていますし、
      実際、現在提供されている WDK でも、この関数を使用したサンプルは一つもありません。)
    もし、この関数を使用する理由が「たまたま見つけたから。。。」のレベルであるなら、
    別の方法、例えば上記サイトで解説されている 「IOCTL による手法」で実現されることを
    お勧め致します。

    最後に、

    「ZwMapViewOfSection() コールでなぜ BSOD になるのか?」

    についてですが、この現象は再現性があるようですので、ライブ デバッグあるいは
    ダンプ解析で原因を究明されてはいかがでしょう?
    ライブ デバッグが一番確実だと思いますが、もしデバッグ環境がない、あるいはすぐに
    デバッグ環境を用意出来ないのであれば、ダンプ解析でも十分に原因究明出来ると思います。
    (ただしドライバ開発を行う以上、カーネル モード デバッグ環境は必須であると思います。)

    ダンプ解析手法に関しては、Microsoft の方が下記 Blog で分かりやすく説明されているので、
    そちらを参考にすると良いと思います。

    ------------------------------------------------
    メモリダンプに !analyze -v するまで・前編 ~ ダンプの取り方~
    http://blogs.msdn.com/b/jpwdkblog/archive/2009/06/03/analyze-v.aspx

    メモリダンプに !analyze -v するまで・後編 ~ ダンプを開く~
    http://blogs.msdn.com/b/jpwdkblog/archive/2009/06/11/analyze_2d00_v2.aspx
    ------------------------------------------------

    もしかしたら、"!analyze -v" するだけで、すぐに原因が分かっちゃうかも?

    以上、参考になりましたら幸いです。

    2011年3月4日 13:06
  • VRE様 ご回答ありがとうございます。

    カーネルモードのデバッグ環境はありまして、以下のような情報を出力し、同様な事例がないか、投稿した次第でした。

    -------

    KERNEL_DATA_INPAGE_ERROR (7a)
    The requested page of kernel data could not be read in.  Typically caused by
    a bad block in the paging file or disk controller error. Also see
    KERNEL_STACK_INPAGE_ERROR.
    If the error status is 0xC000000E, 0xC000009C, 0xC000009D or 0xC0000185,
    it means the disk subsystem has experienced a failure.
    If the error status is 0xC000009A, then it means the request failed because
    a filesystem failed to make forward progress.
    Arguments:
    Arg1: 00000001, lock type that was held (value 1,2,3, or PTE address)
    Arg2: d0000006, error status (normally i/o status code)
    Arg3: 85eead40, current process (virtual address for lock type 3, or PTE)
    Arg4: c0011000, virtual address that could not be in-paged (or PTE contents if arg1 is a PTE address)

    Debugging Details:
    ------------------


    ERROR_CODE: (NTSTATUS) 0xd0000006 - "0x%08lx"

    BUGCHECK_STR:  0x7a_d0000006

    DEFAULT_BUCKET_ID:  VISTA_DRIVER_FAULT

    PROCESS_NAME:  xxxxxxxxxxxxxx

    CURRENT_IRQL:  2

    LAST_CONTROL_TRANSFER:  from 832e8e71 to 83277394

    00 9aff33cc 832e8e71 00000003 ef7a864e 00000065 nt!RtlpBreakWithStatusInstruction (FPO: [1,0,0])
    01 9aff341c 832e996d 00000003 c0603000 85a9b1b0 nt!KiBugCheckDebugBreak+0x1c
    02 9aff37e0 832c9db2 0000007a 00000001 d0000006 nt!KeBugCheck2+0x68b
    03 9aff3824 832cbebb 00000000 c0011000 00000002 nt!MiMakeSystemAddressValid+0x1ec
    04 9aff3844 832703c0 c0600088 85eead40 00000000 nt!MiMakePdeExistAndMakeValid+0x6d
    05 9aff38ac 83379b11 85eead40 88ff07b8 85a7c370 nt!MiInsertViewOfPhysicalSection+0x1c8
    06 9aff38e4 8347a8b4 85eead40 9aff39d8 85a9b1b0 nt!MiMapViewOfPhysicalSection+0x34f
    07 9aff394c 8348b1b1 85eead40 9aff39d8 00000000 nt!MiMapViewOfSection+0x1f9
    08 9aff397c 8348b0dc 8bc0b8e0 85eead40 9aff39d8 nt!MmMapViewOfSection+0x2a
    09 9aff39f8 8324f42a 000000c0 ffffffff 9aff3b14 nt!NtMapViewOfSection+0x204
    0a 9aff39f8 8324d6c5 000000c0 ffffffff 9aff3b14 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ 9aff3a28)
    0b 9aff3a98 99770b1c 000000c0 ffffffff 9aff3b14 nt!ZwMapViewOfSection+0x11 (FPO: [10,0,0])

    ---------

    2011年3月7日 4:39
  • ご提示いただいたログをみると、BugCheck Code が 0x7A (KERNEL_DATA_INPAGE_ERROR)
    となっていますので、Page-in の処理で失敗しているようですが、一番の問題は IRQL だと思います。

    デバッグ ログに、以下の出力があります。

    CURRENT_IRQL:  2

    これは、BSOD を引き起こしたスレッドを処理しているときの IRQL が DISPATCH_LEVEL (2)
    であることを示していると思いますが、ZwMapViewOfSection() をコールしても良いのは、
    PASSIVE_LEVEL (0) のときだけです。
    この制限は、WDK Help Document や下記 MSDN サイトでも明記されており、また前回の返信で
    提示させていただいたサポート技術情報の解説でも触れられていると思います。

    ------------------------------------------------------------
    ZwMapViewOfSection

    http://msdn.microsoft.com/en-us/library/ff566481(VS.85).aspx
    ------------------------------------------------------------

    つまり今回ご投稿されている問題の場合、ZwMapViewOfSection() をコールする際に、
    512KB の場合は、IRQL == PASSIVE_LEVEL の状態であっても、たまたま運よく
    マップするサイズが小さかったので Page Fault を発生させる必要がなかっただけであり、
    8MB の場合はマップする View サイズとの関係上、必ず Page Fault を発生させる
    必要があったため、NT カーネルが明示的にクラッシュさせた。。。
    ということだと思います。
    もっと言えば、マップする View サイズが 512KB であっても、Pool が逼迫あるいは枯渇して
    いる場合には、BSOD になっていてもおかしくない。。。ということではないでしょうか?

    現在の IRQL は KeGetCurrentIrql() で取得することが可能なはずです。
    なので以下のような感じで、IRQL == PASSIVE_LEVEL の場合にのみ、
    ZwMapViewOfSection() をコールするように実装を変更すれば、とりあえずは
    これが問題の BSOD は発生しなくなると思います。
    (もっとも、先の返信でも触れましたが、そもそもこの関数は使わないほうがいい。。。
      と、個人的には思っていますけど。。。)

    ------------------------------------------------------------
    if ( KeGetCurrentIrql() == PASSIVE_LEVEL )
    {
        ....
        ZwMapViewOfSection(...);
        ....
    }
    else
    {
        // Error
    }
    ------------------------------------------------------------

    以上、参考になりましたら幸いです。

    • 回答としてマーク ryo_dev 2011年3月9日 1:12
    2011年3月7日 5:59
  • VRE様 ご回答ありがとうございます。

    試してみます。

    ありがとうございました。

     

    試してみました。

    WdfWorkItemCreate関数等でPassive_Levelにした中でZwMapViewOfSection関数を実行しました。

    8MBサイズでも成功しました。が、しかし、これで取得したアドレスをアプリに渡したのですが、

    ハードのメモリにマッピングされていないようです。

    残念ですが、別の実装を検討します。

    ありがとうございました。

     

    • 回答としてマーク ryo_dev 2011年3月9日 1:11
    2011年3月7日 6:41