8

PCI バスをスキャンして、特定のベンダーから特定のデバイスに関する情報を取得する必要がありました。私の目標は、i2c 転送を行い、さまざまなセンサーからの情報を表示するために、そのカードの PCI メモリをユーザー空間にマップするために、AMD グラフィックス カードの PCI リージョン サイズを見つけることです。

PCI バスをスキャンするために、約 1 年前に Windows x64 用の pciutils 3.1.7 をダウンロードしてコンパイルしました。おそらくDirectIOを使用しています。

これは私のコードです。

int scan_pci_bus()
{
    struct pci_access *pci;
    struct pci_dev *dev;
    int i;

    pci = pci_alloc();
    pci_init(pci);

    pci_scan_bus(pci);

    for(dev = pci->devices; dev; dev = dev->next) 
    {
        pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_PHYS_SLOT);
        if(dev->vendor_id == 0x1002 && dev->device_id == 0x6899)
        {
            //Vendor is AMD, Device ID is a AMD HD5850 GPU
            for(i = 0; i < 6; i++) 
            {
                printf("Region Size %d %x ID %x\n", dev->size[i], dev->base_addr[i], dev->device_id);
            }
        }
    }


    pci_cleanup(pci);

    return 0;
}

私のprintf行でわかるように、いくつかのデータを印刷しようとしましたが、正常に印刷device_idされましたが、このデバイスのPCI領域サイズを含む必要base_addrsizeあるのは常に0です.ループからのサイクルの少なくとも1つは、サイズ > 0。

私のコードは、同じコードを使用する Linux アプリケーションに基づいていますが、Linux に付属の pci.h ヘッダーを使用しています (pciutils には明らかに同じ API があります)。どうやら、Windows(私の場合はWindows 7 x64)はこの情報を表示しないか、少なくともPCIUtilsに公開されていません。

この情報をどのように入手することを提案しますか? Windows 用の pciutils に代わるものがあり、この情報を提供する場合は、それらへのリンクを喜んで取得します。

編集:私はまだ解決策を見つけていません。私の問題に対する解決策があり、32ビットWindowsでも機能する場合は、深く感謝します.

4

2 に答える 2

4

これがどのように機能するかはかなり複雑です。PCI デバイスはBase Address Registers、BIOS とオペレーティング システムがメモリ領域を配置する場所を決定できるようにするために使用されます。各 PCI デバイスは、必要な複数のメモリまたは IO 領域を指定することができ、BIOS/OS に配置場所を決定させます。複雑なことに、サイズとアドレスの両方を指定するために使用されるレジスタは 1 つしかありません。これはどのように作動しますか?

カードに最初に電源を入れると、その 32 ビット アドレス レジスタには 0xFFFF0000 のようなものが含まれます。バイナリ 1 は「OS がこれを変更できる」ことを意味し、バイナリ 0 は「ゼロのままでなければならない」ことを意味します。したがって、これは、上位 16 ビットのいずれかを OS が望むものに設定できることを OS に伝えていますが、下位 16 ビットはゼロのままにしておく必要があります。これは、このメモリ領域が 16 ビットのアドレス空間、つまり 64k を占有することも意味します。このため、メモリ領域はそのサイズに合わせて配置する必要があります。カードが 64K のアドレス空間を必要とする場合、OS はそれを 64K の倍数のメモリ アドレスにしか配置できません。OS がこのカードの 64K メモリ空間を配置する場所を決定すると、このレジスタに書き戻し、そこにあった最初の 0xFFFF0000 を上書きします。

つまり、カードはメモリに必要なサイズ/アライメントを OS に通知し、OS はその同じレジスタ/変数をメモリのアドレスで上書きします。これが完了すると、アドレスをリセットせずにサイズをレジスタから戻すことはできません。

これは、リージョンの大きさをカードに尋ねるポータブルな方法がないことを意味します。尋ねることができるのは、リージョンがどこにあるかだけです。

では、なぜこれが Linux で機能するのでしょうか。カーネルにこの情報を要求しているためです。カーネルには、lspci が機能するのと同じ方法で、この機能を提供するための API があります。私は Windows の専門家ではありませんが、アプリケーションが Windows カーネルにこの情報を問い合わせる方法を知りません。何らかの形でこれを行うための API があるかもしれませんし、カーネル側で実行してこの情報を返す何かを書く必要があるかもしれません。libpci ソースを見ると、Windows の場合、「汎用」バージョンの pci_fill_info() が呼び出され、次の値が返されます。

return flags & ~PCI_FILL_SIZES;

これは基本的に、「あなたが要求したものはすべて返品しますが、サイズは除きます」という意味です.

しかし、とにかくこれは問題ではないかもしれません。I2C レジスタの読み取り/書き込みのみを行う場合、それらは通常 (常に?) 制御/構成領域の最初の 4K にあります。おそらく 4K (1 ページ) をマッピングするだけで、さらに多くのページがある可能性は無視できます。また、このカードの実際のドライバーによる読み取り/書き込みを停止するには、追加の手順が必要になる場合があることに注意してください。I2C バスを手動でビットバンギングしているときに、ドライバーが同時にそれを試みた場合、バスが混乱する可能性があります。

radeon ドライバーに I2C 要求を実行するように依頼する既存の方法もあるかもしれません。これにより、このすべてが回避される可能性があります。

(また、64 ビット アドレス、I/O 空間などを含む、BAR がどのように機能するかについて、多くの詳細を簡略化して説明していることに注意してください。詳細を知りたい場合は、PCI ドキュメントをお読みください)

于 2012-11-22T23:42:18.360 に答える
3

whamma は非常に良い答えを出しました [しかし] 彼が間違っていたことが 1 つあります。それは領域のサイズです。領域サイズは非常に簡単に見つけることができます。ここでは 2 つの方法を示します。1 つ目はバーのアドレスから解読する方法、2 つ目は Windows ユーザー インターフェイスを使用する方法です。

E2000000 がベース レジスタのアドレスであると仮定しましょう。これをバイナリに変換すると、次のようになります: 1110001000000000000000000000000

ここには全部で 32 ビットあります。必要に応じて数えることができます。BAR のビットがどのように配置されているかよくわからない場合は、ここを参照してください -> http://wiki.osdev.org/PCI、具体的には「ベース アドレス レジスタ」、より具体的には「メモリ空間 BARレイアウト"。次に、右端から左端までビットを読み始め、上で示したリンクの画像をガイドとして使用します。

したがって、右から始まる最初のビット (ビット 0) は 0 であり、これがメモリ アドレス BAR であることを示します。ビット (1 ~ 2) は 0 で、32 ビット (これはサイズではないことに注意してください) メモリ BAR であることを示します。ビット 3 は 0 で、プリフェッチ可能なメモリではないことを示します。ビット 4 ~ 31 はアドレスを表します。

このページには、PCI 承認プロセスが記載されています。

PCI デバイスが必要とするアドレス空間の量を決定するには、BAR の元の値を保存し、すべて 1 の値をレジスタに書き込み、それを読み戻す必要があります。次に、情報ビットをマスクし、ビットごとの NOT (C では「~」) を実行し、値を 1 増やすことによって、メモリの量を決定できます。次に、BAR の元の値を復元する必要があります。BAR レジスタは自然にアラインされているため、設定されているビットのみを変更できます。

もう 1 つの方法は、デバイス マネージャーを使用する方法です。[スタート] -> [デバイス マネージャー] -> [ディスプレイ アダプター] -> ビデオ カードを右クリック -> [プロパティ] -> [リソース] を選択します。「メモリ範囲」とマークされた各リソース タイプはメモリ BAR である必要があり、ご覧のとおり、[開始アドレス] から [終了アドレス] と表示されます。たとえば、[00000000E2000000 - 00000000E2FFFFFF] を読み取って、[開始アドレス] から [終了アドレス] を取得するとします。

于 2013-01-12T16:22:35.937 に答える