PCI (Peripheral Component Interconnect)とは、その名前から分かるように、 周辺機器(peripheral)とシステムの接続方法について、構造と制御方法を解説した 規格である。この規格( [3, PCI Local Bus Specification]) では、システム上の機器を電気的に接続する方法と、それらが振る舞うべき方法とが 述べられている。この章では、Linux カーネルがシステムの PCI バスとデバイスを 初期化する方法について説明する。
図表(6.1) PCI バスシステムの例
図表(6.1) では、PCI ベースのシステム例を論理的なダイアグラムで示している。 PCI バスと PCI-PCI ブリッジとは、システム機器を相互に接続する仕組み(glue)で ある。 CPU は、ビデオデバイスと一緒にプライマリ PCI バスである PCI Bus 0 に接続され ている。特別な PCI デバイスである PCI-PCI ブリッジが、プライマリバスを セカンダリ PCI バスである PCI Bus 1 に接続している。PCI 規格の専門用語では、 PCI bus 1 は、PCI-PCI ブリッジの下流(downstream)であり、PCI Bus 0 は PCI-PCI ブリッジの上流(up-stream)である。 セカンダリ PCI バスには、システムの SCSI とイーサネットデバイスが接続されてい る。物理的には、ブリッジとセカンダリ PCI バスとふたつのデバイスは、これらが 組み合わされ、同じ PCI カード上にすべて含まれている。 システムの PCI-ISA ブリッジは、旧弊な遺物たる ISA デバイスをサポートするもの である。またこのダイアグラムでは、Super I/O チップコントローラが示されている が、これはキーボードやマウス、フロッピーを制御するコントローラである。 (脚注1)
CPU と PCI デバイスとは、両者で共有されたメモリにアクセスする必要がある。 そのメモリは、デバイスドライバが PCI デバイスを制御したり、それらの 間で情報をやり取りするために使用される。通常その共有メモリには、デバイスの コントロールレジスタやステータスレジスタの情報が含まれる。それらのレジス タ情報は、デバイスを制御し、その状態を読みとるために利用される。たとえば、PCI 上の SCSI デバイス (コントローラ) 用のデバイスドライバは、そのステータスレジ スタを読んで、SCSI デバイスが SCSI ディスクに情報ブロックを書き込む準備が出来ているかを調べる。あるいは、 デバイスに電源が入った後、デバイスドライバは、デバイスを起動するために コントロールレジスタに書き込みをしたりする。
CPU のシステムメモリをこの共有メモリの代わりに使用することもできるが、 そうしたことがなされると、PCI デバイスがメモリにアクセスするたびに、CPU は 一時停止し、PCI デバイスがメモリの使用を終えるまで待たなければならなくなる。 メモリへのアクセスも、通常、一度にひとつのシステム機器だけに限られる。 したがって、その場合、システムの処理速度が遅くなる。 また、適切な管理方法のないままシステムの周辺機器にメインメモリへのアクセスを 許すことは、よい考えとはいえない。それは非常に危険な場合がある。悪質なデバイス がシステムを不安定にするからである。
周辺機器は独自のメモリ空間を持っていて、CPU はそれらの空間へアクセスできる。 しかし、デバイス側からのシステムメモリへのアクセスは、DMA(Direct Memory Access) チャンネルを使用して厳しく制御される。ISA デバイスは、ISA I/O(Input/Output)空間 と ISA メモリ空間のふたつのアドレス空間へアクセスできる。PCI には三つあり、PCI I/O 空間と PCI メモリ空間、PCI コンフィグレーション空間(configuration space) へアクセスできる。 これらすべてのアドレス空間は CPU からもアクセス可能である。その際、CPU は、 デバイスドライバを使って PCI I/O 空間と PCI メモリ空間へアクセスし、Linux カーネル内の PCI 初期化コードを使って PCI コンフィグレーション空間へアクセス する。
Alpha AXP プロセッサは、本来、システムアドレス空間以外のアドレス空間へは アクセスできない。それゆえ、Alpha AXP プロセッサは、サポートチップを使用して、 PCI コンフィグレーション空間等の他のアドレス空間にアクセスしている。サポート チップは、スパースアドレスマッピングスキーム( sparse address mapping scheme)を使用して、巨大な仮想アドレス空間の一部 を使い、それを PCI アドレス空間にマップしている。
図表(6.2) PCI コンフィグレーションヘッダ
PCI-PCI ブリッジを含むシステム上のすべての PCI デバイスは、PCI コンフィグ レーションアドレス空間内に設定用のデータ構造を保持している。 システムは、PCI コンフィグレーションヘッダ(PCI configuration header)を使用する ことで、デバイスを識別し制御している。 PCI コンフィグレーションアドレス空間内における ヘッダの正確な位置は、そのデバイスが PCI トポロジーのどこに位置するかに依存す る。たとえば、PC のマザーボード上のある PCI スロットに差された PCI ビデオ カードは、そのスロット番号の位置にコンフィグレーションヘッダを持つことになり、 違うスロットに差されていれば、そのヘッダは、PCI コンフィグレーションアドレス 空間の違うメモリ位置にあることになる。 しかし、そうした位置は問題ではない。というのも、PCI デバイスやブリッジが どこにある場合でも、システムは、それらのコンフィグレーションヘッダ内のステータ スレジスタとコンフィグレーションレジスタ(configuration register)を使用して、 デバイスを検出し、設定するからである。
一般的に言って、システムは、すべての PCI スロットが、ボード上のスロットの 場所に関連したオフセット位置に PCI コンフィグレーションヘッダを持つように設計 されている。 したがって、たとえば、ボードの最初のスロットは PCI コンフィグレーションアドレス 空間をオフセット 0 の位置に持ち、二つ目のスロットはオフセット 256 の位置に 持つ。(ヘッダは、256 バイトのコンフィグレーション空間のなかで、先頭 64 バイトを 占めている。) そしてこれ以降のスロットについても同様にそれぞれの場所が決められ ている。 システム固有のハードウェアのメカニズムを設計する場合、PCI 設定コードが、 すべての PCI バス上にあるすべての PCI コンフィグレーションヘッダを調べることが でき、その際に、ヘッダのフィールド(通常はベンダ識別フィールド)を読み込むことで エラーが取得されるかどうかによって、デバイスがそのバス上にあるのかどうかを確認 できるように設計されなければならない。 規格書( 参考文献 3)では、空の PCI スロットのベ ンダ ID とデバイス ID を読み取ろうとしたときは、0xFFFFFFFF というエラー メッセージだけしか返してはいけないことが記述されている。
図表(6.2)では、256 バイトある PCI コンフィグレー
ションヘッダのレイアウトが示されている。それには、次のようなフィールドが含ま
れる。
[see:
include/linux/pic.h]
その PCI デバイスの製造会社を示す固有の数字である。DEC の PCI ベンダ ID は、 0x1011 であり、Intel は、0x8086 である。
デバイスそのものを示す固有の数字。たとえば、DEC の 21141 ファストイーサネット デバイスは、デバイス ID として 0x0009 を持っている。
このフィールドは、規格上定められた意味を持つビットによって、デバイスの状態 (status)を表すものである。( 参考文献 3)
このフィールドに書き込むことで、システムはデバイスを制御する。たとえば、デバイ スに PCI I/O メモリへのアクセスを許すなど。
これは、そのデバイスのタイプを特定するものである。ビデオ、SCSI など、すべての 種類のデバイスには、規格上のクラスがある。SCSI のクラスコードは 0x0100 である。
このレジスタは、デバイスが使用できる PC I/O 空間と PCI メモリ空間のタイプと 容量と位置とを決定し割り当てるために使用される。
PCI カード上の 4 つの物理ピンが、カードから PCI バスへの割り込み信号を 伝えている。規格では、それらを A, B, C, D とラベル付けしている。割り込みピン フィールドは、PCI デバイスがそれらのうちどのピンを使うかを記述している。 一般的には、それは特定のデバイスごとにハードウェア的に固定されている。 すなわち、システム起動のたびに、デバイスは同じ割り込みピンを使用するわけであ る。この情報により、割り込み処理サブシステムはそのデバイスからの割り込みを管理 できる。
デバイスの PCI コンフィグレーションヘッダ内の割り込みラインのフィールドは、 PCI 初期化コードやデバイスドライバ、そして Linux の割り込み処理サブシステムの 間で割り込みハンドルをやり取りするために使用される。 このフィールドに書かれた番号は、デバイスドライバにとっては無意味である。 しかし、Linux オペレーティングシステム上で、割り込み処理ルーチンが、 PCI デバイ スから発せられた割り込みシグナルを正しいデバイスドライバの割り込み処理コードへ と手渡すために必要である。 Linux の割り込み処理の方法についての詳細は、 「割り込みと割り込み処理」の章を参考にしてほしい。
PCI I/O 空間と PCI メモリ空間のふたつのアドレス空間は、デバイスが、 CPU 上で実行されている Linux カーネル内のデバイスドライバと通信をするために 使用される。 たとえば、DEC チップ 21141 ファーストイーサネットデバイスは、内部レジスタ情報 を PCI I/O 空間にマップする。デバイスドライバは、それらのレジスタを読み書きしな がら、そのデバイスを制御する。ビデオドライバは、一般に巨大な PCI メモリ空間を 使用して、ビデオ情報を保持する。
PCI システムの設定が行われ、さらに PCI コンフィグレーションヘッダ内のコマン ドフィールドを使用して、デバイスの PCI I/O と PCI メモリアドレス空間へのアクセ スが有効と設定されて、初めてそれらのアドレス空間へのアクセスが可能になる。 注意すべきなのは、PCI 設定コードだけが PCI コンフィグレーションアドレス空間に 対する読み書きができるということである。Linux デバイスドライバは、PCI I/O と PCI メモリアドレス空間に対してだけしか読み書きできない。
PCI-ISA ブリッジは、PCI I/O 空間と PCI メモリ空間へのアクセスを ISA I/O 空間 と ISA メモリ空間へのアクセスに変換することで、古い ISA デバイスをサポートして いる。現在販売されている多くのシステムには ISA スロットと PCI スロットの両方が いくつか付属している。しばらくすれば、この下位互換性の必要は減少して、PCI のみ のシステムが販売されるだろう。 システムの ISA デバイスが ISA アドレス空間(I/O 空間とメモリ空間)内でレジスタを 保持している場所は、今では遠い昔となっている初期の Intel 8088 ベースの PC 時代 からずっと固定されたままである。5000 ドルの Alpha AXP ベースのコンピュータ システムでさえ、ISA フロッピーコントローラが置かれる ISA I/O 空間内の位置は、 初期の IBM PC と同じである。PCI の仕様では、それに対処するために、PCI I/O と PCI メモリアドレス空間の下位アドレス領域をシステムの ISA 周辺機器用に予約し、 単一の PCI-ISA ブリッジを使用してそれらの領域にある PCI メモリ空間へのアクセス を ISA メモリ空間へのアクセスに変換している。
PCI-PCI ブリッジは、システム上にある複数の PCI バスを結合するための特別な PCI デバイスである。シンプルなシステムは、単一の PCI バスしか持たないが、単一 の PCI バスでサポートできる PCI デバイスの数には電気的な制限がある。PCI-PCI ブリッジを使用して多くの PCI バスを加えれば、システムは、より多くの PCI デバイスをサポートできる。これは、高いパフォーマンスを要求されるサーバの場合 特に重要である。もちろん、Linux は、PCI-PCI ブリッジを完全にサポートしている。
PCI-PCI ブリッジは、PCI I/O 空間と PCI メモリ空間に対する読み書きのリクエス トのサブセットを下流領域(downstream)のバスに渡すだけである。 たとえば、 図表(6.1)では、 PCI-PCI ブリッジは、読み出しと書き込みのアドレスが、SCSI かイーサネットが所有 する PCI I/O アドレス空間か PCI メモリアドレス空間へのものであるならば、その アドレスを PCI バス 0 から、PCI バス 1 に渡すだけである。それ以外の PCI I/O アドレス空間と PCI メモリアドレス空間に関するアドレスはすべて無視される。 このフィルタリングによって、アドレスが不必要にシステム全体に流れるのを防いで いる。これを実現するためには、PCI-PCI ブリッジは、プライマリバスからセカンダリ バスに渡す必要のある PCI I/O 空間と PCI メモリ空間のベースアドレスとその上限の アドレスとをプログラムされていなければならない。 システム上の PCI-PCI ブリッジの設定が完了すると、Linux デバイスドライバが PCI I/O 空間と PCI メモリ空間に上記の範囲内でアクセスする限り、デバイスドライ バからは、PCI-PCI ブリッジが見えなくなる。これは、Linux の PCI デバイスドライバ の作者にとっては、ドライバの作成が少し楽になるという点で非常に重要な機能で ある。しかし、これは、Linux からすると、後に述べるように PCI-PCI ブリッジの設 定をややトリッキーにする機能でもある。
図表(6.3) Type 0 PCI コンフィグレーションサイクル
図表(6.4) Type 1 PCI コンフィグレーションサイクル
CPU の PCI 初期化コードがメイン PCI バス上にないデバイスを初期化できるように するには、コンフィグレーションサイクル(Configuration cycle)を PCI のプライマリ インターフェイスからセカンダリインターフェイスへと渡すか否かをブリッジが決定で きるような仕組みが存在しなければならない。 コンフィグレーションサイクルは、PCI バス上では単なるアドレスとして表現される。 PCI の規格書では、PCI コンフィグレーションアドレス空間設定のために、Type 0 と Type 1 のふたつのフォーマットが定義されていて、それらは図表(6.3)と図表(6.4)と にそれぞれ示されている。 Type 0 の PCI コンフィグレーションサイクルにはバス番号が含まれない。そして、 Type 0 の PCI コンフィグレーションサイクルは、すべてのデバイスによって、その PCI バス上の PCI コンフィグレーションアドレスであると解釈される。 Type 0 のコンフィグレーションサイクルのビット 31 から 11 までは、 デバイス選択フィールドとして扱われる。システムを設計する際のひとつの方法とし て、個々のビットが違うデバイスを選択するようにする方法がある。その場合、 ビット 11 は、スロット 0 にある PCI デバイスを選択し、ビット 12 は、スロット 1 にある PCI デバイスを選択する等ということになる。もうひとつの方法は、デバイスの スロット番号を直接ビット 31 から 11 の領域に書き込むことである。どちらの方法が 利用されるかは、システムの PCI メモリ空間のコントローラに依存する。
Type 1 の PCI コンフィグレーションサイクルには、PCI バス番号が含まれる。 そして、Type 1 のコンフィグレーションサイクルは、PCI-PCI ブリッジ以外のすべて の PCI デバイスから無視される。 Type 1 のコンフィグレーションサイクルを見たすべての PCI-PCI ブリッジは、それら を、自分の下流(downstream)側に渡すかどうかを選択する。PCI-PCI ブリッジが Type 1 コンフィグレーションサイクルを無視するか、それとも下流の PCI バスに 渡すかは、PCI-PCI ブリッジの設定次第である。 PCI-PCI ブリッジはすべて、プライマリバスインターフェイス番号とセカンダリバス インターフェイス番号とを持っている。個々の PCI-PCI ブリッジにとって、プライマリ バスインターフェイスは、CPU に最も近い側のインターフェイスであり、セカンダリ バスインターフェイスは CPU に最も遠い側になるインターフェイスである。 また、それぞれの PCI-PCI ブリッジは、サブオーディネイトバス番号(subordinate bus number)も 持っている。これは、そのセカンダリバスインターフェイスを越えてブリッジされて いるすべての PCI バスの中で最大のバス番号に該当する。言い換えるとサブオーディ ネイトバス番号は、その PCI-PCI ブリッジの下流に位置する PCI バス番号の なかで最も大きいバス番号である。PCI-PCI ブリッジが Type 1 の PCI コンフィグレー ションサイクルを見るとき、PCI-PCI ブリッジは次のような処理をする。
したがって、もし 図表(6.9)のトポロジーの Bus 3 上のデバイス 1 (D1)と通信したい場合は、Type 1 コンフィグレーションコマンドを CPU から送り出さなければならない。ブリッジ 1 は、これを変更せずに Bus 1 に渡 す。ブリッジ 2 は、これを無視するが、ブリッジ 3 は、これを Type 0 コンフィグ レーションコマンドに変換し、バス 3 上に送信するので、そこにあるデバイス 1 が これに応答する。
PCI の初期化の過程でバス番号を割り当てることは個々のオペレーティングシステム の責任であるが、どのような番号割り当て方法が取られた場合でも、次の説明は システムのすべての PCI-PCI ブリッジに当てはまらなければならない。
「PCI-PCI ブリッジの下流にあるすべての PCI バスは、セカンダリバスのバス番号と サブオーティネイトバス番号との間(その番号を含む)のバス番号を持たなけ ればならない。」
このルールが破られた場合、PCI-PCI ブリッジは、Type 1 コンフィグレーション サイクルを正しく渡したり変換したりできないので、システムはシステム上の PCI デバイスを見つけて初期化することに失敗することになる。 この番号割り当て方法を実現するために、 Linux は、それらの特殊なデバイスである PCI-PCI ブリッジをある決まった順番で 設定する。 「PCI バス番号の割り当て」 のセクションでは、Linux の PCI ブリッジとバス番号当ての方法について、その実際の 例も交えて詳解している。
Linux の PCI 初期化コードは、論理的に 3 つの部分に分かれる。
この仮想デバイスドライバは、Bus 0 から PCI システムの検索を始めて、システム内
のすべての PCI デバイスとブリッジの所在を検出する。そのドライバは、システムの
トポロジーを示すデータ構造体の連結リストを作成する。さらに、発見したブリッジ
すべてに番号付けをする。
[see:
drivers/pci/pci.c,
include/linux/pci.h]
このソフトウェア層(layer)は、
「PCI BIOS の機能」で述べる
サービスを提供する。Alpha AXP は BIOS サービスを持たないが、同様の機能を提供
するコードが Linux カーネルの中にある。
[see: arch/*/kernel/bios32.c](
i386,
alpha)
システム固有のフィックスアップコード(fixup code)は、PCI の初期化におけるシステ
ム固有の設定の隙間を埋める役割を果たす。
[see: arch/*/kernel/bios32.c](
i386,
alpha)
図表(6.5) Linux カーネルの PCI データ構造
Linux カーネルが PCI システムを初期化する際、カーネルは、システムの実際上の PCI トポロジーを忠実に反映するため PCI データ構造体を作成する。上記図表(6.5) は、 図表(6.1)の PCI システムの例に対して作成される データ構造体の相互関係を表したものである。
個々の PCI デバイス(PCI-PCI ブリッジも含む)は、
pci_dev
データ構造体によって記述される。
個々の PCI バスは、
pci_bus
データ構造体で
記述される。その結果、PCI バスのツリー構造が生成され、個々の PCI バスに
いくつかの子 PCI デバイスが繋がる構造ができる。PCI バスには、
(プライマリ PCI バスである Bus 0 を除いて) PCI-PCI ブリッジを使ってのみ到達が
可能なので、個々の pci_bus
構造体には、そのバスへアクセスするための
PCI デバイス(すなわち PCI-PCI ブリッジ)へのポインタが含まれている。
(pci_bus
構造体では)PCI ブリッジは、もとのバスの親に当たる PCI バス
から見て子に該当する。
図表(6.5)では表示されていないが、システム上の
すべての PCI デバイスに対するポインタとして
pci_devices
がある。システム上のすべての PCI デバイスは、
そのキューの上に pci_dev
データ構造体を登録する。
このキューは、Linux カーネルがシステム上のすべての PCI デバイスをすばやく
探すために使用される。
PCI デバイスドライバは、実際にはデバイスドライバではなく、システムの初期化
の際にコールされるオペレーティングシステムの関数である。PCI 初期化コードは、
システム上のあらゆる PCI バスをスキャンして、すべての PCI
デバイスを捜す(これには、PCI-PCI ブリッジデバイスも含まれる)。
[see: Scan_bus(), in
drivers/pci/pci/c]
PCI 初期化コードは、PCI BIOS のコードを使用して、現在スキャン中の PCI バス
上に存在するスロットが占有されているかどうかをすべて調べる。PCI スロットが占有
されている場合、PCI 初期化コードは、
pci_dev
構造体を作成してそのデバイスを記述し、その構造体を(
pci_devices
によってポイントされた)既知の PCI デバイスのリストに
リンクする。
PCI 初期化コードは、PCI バスの Bus 0 をスキャンすることから始める。それは、
存在する全 PCI スロットの全 PCI デバイスに対して、そのベンダ ID とデバイス
ID を読み出そうとする。占有されたスロットを見つけたら、
pci_dev
データ構造体を作成し、デバイス情報を書き込
む。PCI 初期化コードによって作成された pci_dev
データ構造はすべて(す
べての PCI-PCI ブリッジも含めて)、単純な連結リストである pci_devices
にリンクされる。
発見された PCI デバイスが PCI-PCI ブリッジであった場合は、
pci_bus
データ構造体が作成され、pci_bus
と
pci_dev
データ構造体から成るツリーにリンク
される。そのツリーは、
pci_root
によって参照される。PCI-PCI ブリッ
ジは、クラスコード
(class code) 0x060400 を
持っているので、PCI 初期化コードは、PCI デバイスが PCI-PCI ブリッジであるかど
うかを判別できる。
次に、Linux カーネルは、発見された PCI-PCI ブリッジの反対側(下流側)にある
PCI バスを設定する。そのバス上でさらに PCI-PCI ブリッジが発見された場合、それら
にも設定がなされる。このプロセスは、デプスワイズアルゴリズム(depthwise
algorithm)と呼ばれる。システムの PCI トポロジーは、まずひとつの階層が一番奥まで
マップされてから、並列する部分の検出が行われる。
図表(6.1)では、Linux は、まず PCI Bus 1 にあるイーサネットと SCSI
デバイスを設定してから、PCI Bus 0 にあるビデオデバイスを設定する。
Linux が下流の PCI バスを検出する際は、介在する PCI-PCI ブリッジの セカンダリバス番号とサブオーディネイトバス番号も設定しなければなら ない。これについては、以下の 「PCI-PCI ブリッジの設定: PCI バス番号の割り当て」で詳しく説明する。
図表(6.6) PCI システムの設定: Part 1
PCI-PCI ブリッジを介して PCI I/O アドレス空間、PCI メモリアドレス空間、 または PCI コンフィグレーションアドレス空間への読み書きを行うためには、各 PCI-PCI ブリッジについて以下の情報が必要である。
PCI-PCI ブリッジの直上のバス番号
PCI-PCI ブリッジの直下のバス番号
ブリッジの下流に位置する到達可能なバスの中で、最も大きなバス番号
PCI-PCI ブリッジの下流にある、PCI I/O アドレス空間と PCI メモリアドレス 空間のすべてのアドレスに対するウィンドウのベースアドレスとそのサイズ
問題は、ある PCI-PCI ブリッジを設定しようとするとき、そのブリッジに関する サブオーディネイトバス番号をその時点では知り得ないということである。 さらに下流の PCI-PCI ブリッジがあるかどうか分からず、分かったとしてもそれらに 割り当てる番号は分からない。 解決策は、デプスワイズ再帰的アルゴリズム(depthwise recursive algorithm)を使用して PCI-PCI ブリッジの個々のバスをスキャンして、発見された 時点で番号を当てていくことである。 個々の PCI-PCI ブリッジが発見されて、そのセカンダリバス番号が割り当てられたら、 それに一時的なサブオーディネイトバス番号として 0xFF を割り当てておいて、その 下流にあるすべての PCI-PCI ブリッジをスキャンして番号を割り当てる。 こうした処理全体は複雑に思えるかもしれないが、下記の実際の例を見れば、その プロセスが分かりやすくなるだろう。
図表(6.6)のトポロジーを使うと、スキャンによって、 最初のブリッジとして Bridge 1 が発見される。Bridge 1 の下流に位置する PCI バス は、バス 1 と番号付けられ、Bridge 1 は、セカンダリバス番号として 1 が、一時的な サブオーディネイトバス番号として OxFF が割り当てられる。 これは、PCI バス番号として 1 かそれ以上を指定するType 1 の PCI コンフィグレー ションアドレスが、Bridge 1 を通って PCI バス 1 に渡されることを意味する。 それらは、バス番号 1 を持つ場合は Type 0 のコンフィグレーションサイクルに変換 されるが、それ以外の番号の場合は変換されない。このことは、PCI バス 1 に降りて スキャンするために、Linux PCI 初期化コードがしなければならないことそのもので ある。
図表(6.7) PCI システムの設定: Part 2
Linux は、デプスワイズアルゴリズム(depthwise algorithm)を使うので、初期化コード は PCI バス 1 へと進んでスキャンする。ここで、初期化コードは、PCI-PCI Bridge 2 を発見する。PCI-PCI ブリッジ 2 以下には PCI-PCI ブリッジは存在しないので、それ にはサブオーディネイトバス番号 2 が割り当てられ、それはそのセカンダリインター フェイスに割り当てられた番号と合致する。 図表(6.7)では、バスと PCI-PCI ブリッジとがこの時点で番号付けされたことを示している。
図表(6.8) PCI システム設定: Part 3
PCI 初期化コードは、PCI バス 1 のスキャニングに戻り、もうひとつの PCI-PCI ブリッジである Bridge 3 を発見する。それは、そのプライマリバスインターフェイス として 1 を割り当てられ、セカンダリインターフェイスの番号として 3 を、 サブオーディネイトバス番号として 0xFF を割り当てられる。 図表(6.8)では、システムが現在どのように設定されたの かを示している。バス番号 1, 2, 3 についての Type 1 の PCI コンフィグレーション サイクルは、適切な PCI バスに対して正しく伝達される。
図表(6.9) PCI システムの設定: Part4
Linux は、PCI-PCI ブリッジ Bridge 3 の下流にある PCI バス 3 のスキャニング を始める。PCI バス 3 は、次の PCI-PCI ブリッジ(Bridge 4)をバス上に持っている ので、それは、プライマリバス番号として 3 を、セカンダリバス番号として 4 を割り 当てられる。これはそのバス上の最後のブリッジなので、ブリッジにはサブオーディネ イトバスインターフェイス番号 4 が割り当てられる。 初期化コードは、PCI-PCI Bridge 3 に戻り、それにサブオーディネイトバス番号 4 を 割り当てる。最後に、PCI 初期化コードは、PCI-PCI Bridge 1 にサブオーディネイト バス番号 4 を割り当てることができる。 図表(6.9)では、最後のバス番号が示されている。
PCI BIOS 関数は、全プラットフォームに共通な、一連の標準ルーチンである。
たとえば、それらは、Intel や Alpah AXP ベースのシステムの両方で同一である。
PCI BIOS 関数は、PCI アドレス空間への CPU で制御されたアクセスを可能にする。
[see: arch/*/kernel/bios32.c](
i386,
alpha )
それらを使用できるのは、Linux カーネルコードとデバイスドライバだけである。
Alpah AXP の PCI フィックスアップ(fixup)コードは、Intel のもの(Intel 版の
コードは基本的に何もしない)以上の役割を果たす。
[see: arch/*/kernel/bios32.c](
i386,
alpha )
Intel ベースのシステムでは、ブート時に実行されるシステム BIOS が、PCI
システムを完全に設定する。そのために、Linux は、そこでの設定をマップする以外
はほとんどすることがない。Intel ベースでないシステムの場合、次のようなさら
なる設定作業がなされる必要がある。
次のサブセクションでは、それらのコードの働きを説明する。
検出された PCI デバイスは、その PCI I/O と PCI メモリがアドレス空間をどの 程度必要とするのか調べるために、検査される。その検査のために、個々のベース アドレスレジスタには、すべて 1 が書き込まれ、その後で読み出される。デバイスは、 使用しないアドレスのビットに関しては 0 を返すので、必要とされるアドレス空間を 効率的に確定することができる。
図表(6.10) PCI コンフィグレーションヘッダ: ベースアドレスレジスタ
ベースアドレスレジスタには、基本的にふたつのタイプがある。ひとつは、デバイス レジスタが、PCI I/O もしくは PCI メモリ空間のどの範囲になければならないかを示 す。これは、レジスタのビット 0 によって示される。 図表(6.10)では、PCI メモリと PCI I/O についての 二種類のフォームのベースアドレスレジスタが示されている。
そのベースアドレスレジスタがどの程度のアドレス空間を要求しているのかを正確に 知るために、そのレジスタにすべて 1 を書き込んで、そのあとでそれを読み出す。 デバイスは、使用しないアドレス空間ではビット値 0 を返すので、効果的に必要な アドレス空間を確定できる。こうした設計方法によって、利用されるアドレス空間全体 は 2 の n 乗となり、自然な境界に整合する位置に置かれることになる。
たとえば、DEC チップ 21142 PCI ファーストイーサネットデバイスを初期化すると き、デバイスは、PCI I/O か PCI メモリのいずれかのスペースとして 0x100 バイトが 必要であると告げるとする。初期化コードは、デバイスにその空間を割り当てる。 空間を割り当てた瞬間、21142 のコントロールレジスタとステータスレジスタが、 それらのアドレス上に存在するのがわかるようになる。
すべてのメモリと同様に、PCI I/O と PCI メモリ空間は有限であり、ときにやや 不足することもある。Intel 以外のシステムにおける PCI フィックスアップコード (fixup code)(および、Intel システムにおける BIOS コード)は、個々のデバイスに 対して、それが要求する量のメモリを効率的に割り当てなければならない。 一方、PCI I/O と PCI メモリ空間のアドレスは、自然な境界に整合するような方法 で各デバイスに割り当てられなければならない。 たとえば、デバイスが PCI I/O 空間に 0xB0 バイトの容量を要求する場合、それは 0xB0 の倍数となるアドレス上に割り当てられる必要がある。 これに加えて、各ブリッジに関する PCI I/O と PCI メモリのベースアドレス は、それぞれ 4 k と 1 M バイトの境界に整合しなければならない。 下流に位置するデバイスのアドレス空間は、上流にあるすべてのブリッジが下流デバイ ス用として確保するメモリ領域内になければならない。 したがって、空間を効率的に割り当てるのは、やや難しい問題である。
Linux が使っているアルゴリズムでは、PCI デバイスドライバによって作成された
バスとデバイスとのツリー構造によって記述される個々のデバイスに対して、PCI
I/O 空間および PCI メモリ空間にそれぞれが使うアドレスを昇順に割り当てるという
方法でこの問題に対処している。
この場合も、再帰的アルゴリズムが使用され、PCI 初期化コードによって作成された
pci_bus
と
pci_dev
データ構造体が順番に調べられる。
ルート PCI バス(これは、
pci_root
によって
ポイントされている)から調査を始めて、BIOS 関数やフィックスアップコードは次の
ようなことをする。
図表(6.1)にある PCI システムを例に取ると、 PCI フィックスアップコードは、次のような方法でシステムを設定することになる。
ベースアドレスについて、PCI I/O を 0x4000 とし、PCI メモリを 0x100000 とする。 これによって、PCI-ISA ブリッジは、これより下位のアドレスを ISA アドレスサイクル に変換できるようになる。
このデバイスは、PCI メモリの容量として 0x200000 を要求している。したがって、 要求されたサイズのメモリを連続的に割り当てる必要があるので、その容量を 現在の PCI メモリのベースアドレスである 0x200000 から始まる領域に割り当てる。 これによって、PCI メモリのベースアドレスは 0x400000 に移動し、PCI I/O の ベースアドレスは 0x4000 のままである。
この時点で、PCI-PCI ブリッジを通過して、その下流にあるデバイスに PCI メモリ を割り当てる。注意: ベースアドレスは既に正しく設定されており、ここで再設定する 必要はない。
このデバイスは、PCI I/O と PCI メモリ空間両方に 0xB0 バイトを要求している。 したがって、0x4000 に PCI I/O が、0x400000 に PCI メモリが割り当てられる。 PCI メモリのベースアドレスは、0x4000B0 に移動し、PCI I/O ベースアドレスは 0x40B0 に移動する。
このデバイスは、PCI メモリに 0x1000 を要求しているので、それが自然な境界に 沿って配置されるように、0x401000 に必要な空間を割り当てられる。 PCI I/O ベースアドレスは、そのまま 0x40B0 であり、PCI メモリのベースアドレス は、0x402000 に移動する。
ここで、ブリッジに戻って、PCI I/O ウィンドウを 0x4000 と 0x40B0 の間に設定し て、その PCI メモリウィンドウを 0x400000 と 0x402000 との間に設定する。 これは、PCI-PCI ブリッジがビデオデバイスのための PCI メモリアクセスを無視 して、それらがイーサネットか SCSI デバイスのためのものなら通すということを 意味する。
(脚註1)たとえば?