PCI Express

base version : v0.1.0

PCI Expressとは

高速なI/Oデバイスを繋ぐための規格。

グラボやLANカード等を差すのに使われる。

実際にPCI Expressスロットに刺さってなくても、内部バスとしてはPCI Expressな事も多いので注意。(チップセット内蔵の有線LANも、PCI Express経由で通信する)

I/Oデバイスをサポートするなら、これがないと始まらない。

仕様

→参考資料

PCI Local Bus Specification Revision 3.0がPCI(not PCI Express)の仕様で、PCI Express Base Specification Revision 3.0がPCI Expressの仕様。ただし、共通な部分も多く、PCI Express Base Specification Revision 3.0では被ってる部分について解説してくれていない。だから実装にはPCI Local Bus Specification Revision 3.0も読む必要アリ

PCI Expressデバイスへのアクセス

各PCI Expressデバイスの情報はConfiguration Spaceから取得できる。Configuration Spaceは、PCI Expressデバイスのレジスタ、と思えば良い。Configuration Spaceについては、PCI仕様のChapter 6 : Configuration Spaceや、PCI Express仕様の7.2 : PCI Express Configuration Mechanismsに詳しく書かれている。

また、PCI ExpressではConfiguration SpaceにメモリマップドI/Oでアクセスでき、PCI Express Enhanced Configuration Access Mechanism(ECAM)を使ってレジスタアドレスを計算する事ができる。これは7.2.2に記載されている。→kernel/dev/pci.h#L75

レジスタにアクセスするには、Configuration Spaceそれ自体がメモリ上のどこに配置されているかを知らなければならないのだが、これはACPIのMCFGテーブルから取得するのが良い。(他にも取得方法はあるが)→kernel/dev/pci.cc#L48

デバイスの特定

デバイスを動かすためには、適切なドライバが必要である。あるデバイスがどのドライバで動くかは、Device ID、及びVendor IDで識別できる。一度デバイスドライバに処理を投げて、デバドラ側でDevice ID、Vendor IDを取得し、デバドラが保有しているテーブル上のIDとマッチすればデバイス初期化、というのが基本的な流れである。→kernel/dev/pci.cc#L58

ちなみに、手動で調べる場合はこちら。http://pcidatabase.com/

また、Vendor IDが0xFFFFの場合は、デバイスが繋がっていない、と考えて良い。(PCI仕様の6.2.1. Device Identificationより)

Base Address Registers

Configuration Spaceの中で恐らく最も大事なのがBase Address Registers(BAR)である。BARはConfiguration SpaceはPCIに関する各種レジスタを格納しているが、デバイスはそれとは別にデバイス固有のレジスタを保有しており、そのアドレスをBARから取得できる。BARは複数エントリ存在するが、どのエントリがどのレジスタ空間を差すかはデバイス毎に異なり、またBARエントリによってメモリマップドかI/O空間を叩かなければいけなかったりする。(フラグがあるので、それを参照)

割り込み(MSI/MSI-X)

PCIデバイスの割り込みには二種類あり、MSI(&MSI-X)とLegacyな割り込みがある。Legacy割り込みが割り込みピンを共有している一方、MSIは直接目的のCPUに割り込みを発生させられる。その上、MSIの方が設定が明らかに楽なので、よっぽどの事がない限りはMSIをサポートすれば良い。よっぽどの事というのは、QEMUのエミュレーションデバイスがレガシー割り込みしかサポートしてない、とかね。

Raph_Kernelでは現状MSI-Xをサポートしていないため、MSI割り込みの設定方法のみを記載する。(MSI-Xも基本的には似たような感じで設定できたはず)

kernel/dev/pci.cc#L100

まず、Capabilities List(PCI仕様6.7 : Capabilities List)を走査して、MSI Capability Structure(PCI仕様6.8.1 : MSI Capability Structure)を見つける。このStructure内に割り込みアドレスを設定するフィールドがあるので、それを設定し、MSIを有効化すれば割り込みが飛んでくるようになる。

割り込み(Legacy)

osdev.orgより。

If you plan to use the I/O APIC, your life will be a nightmare.

だそうで。

ざっくり説明すると、ACPIから頑張って取得して、振り分ければ良いのだが、ACPIから頑張って取得する部分がなかなか難儀。ACPICAを使って取得しましょう。しかも頑張って取得した所で、その割り込み線は他のデバイスと共有しているっていうね。デバイス固有の割り込みじゃないのかよ、と。

参考になる(参考にすべき)サイト集:

http://www.valinux.co.jp/technologylibrary/document/linux/interrupts0002/

http://www.tldp.org/HOWTO/Plug-and-Play-HOWTO-7.html

https://people.freebsd.org/~jhb/papers/bsdcan/2007/article/article.html

Raph KernelではLegacy PCI割り込みのIRQ割り込み番号をAcpiCtrl::GetPciIntNumで取得している。この関数はまずInterrupt Pin(PCI仕様 : 6.2.4 Miscellaneous Registers)を取得し(INTA# or INTB# or INTC# or INTD#)、ACPIオブジェクトのInterrupt Pinと対応する_PRTエントリ(ACPI仕様6.2.12 : _PRT (PCI Routing Table))をACPICAのAcpiGetIRQRoutingTable関数(ACPICA仕様8.9.5 :AcpiGetIRQRoutingTable)で読み、割り込み番号を取得する。

正直、ACPICAを使っても_PRTエントリの取得は複雑怪奇なので、ACPICAのサンプルコードのソースコードを読むのが一番手っ取り早い。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中