[Linux] PCI endpoint の設定

Linux で PCI-endpoint を動かし、PCI-host と接続してテストする場合の設定例を示します。PCI-endpoint は通常デバイス(Videoカードやinterfaceカードなど)ですが、Linux で endpoint として動作をさせることができます。ただし、ここでの設定例はメモリと割込みによるテストの実施のみ行います。endpoint デバイスとして見せるにはさらに上位の function ドライバが必要です。

EP 側: kernel 再構築

endpoint 側のドライバを有効にして、kernel をビルドし直します。この他に搭載されているコントローラのドライバも有効にしてください。

CONFIG_PCI_ENDPOINT=y
CONFIG_PCI_ENDPOINT_CONFIGFS=y
CONFIG_PCI_EPF_TEST=y

EP 側: function の設定

PCI endpoint も usb_gadget と同様に configfs を使います。configfs がマウントされていない場合は先にマウントしてください。

mount -t configfs none /sys/kernel/config

endpoint 側にて function の設定を行います。ここでは "pci_epf_test" が function になり、その下に任意名でディレクトリを作成して、各パラメータを設定します。

cd /sys/kernel/config/pci_ep
mkdir -p functions/pci_epf_test/HOGE
echo 0x1234 > functions/pci_epf_test/HOGE/vendorid
echo 0x5678 > functions/pci_epf_test/HOGE/deviceid
echo 8 > functions/pci_epf_test/HOGE/msi_interrupts

ベンダID、プロダクトIDなどを設定します。この情報は PCI-SIG で管理された情報を使いますが、RC 側が認識するためにテストドライバ(pci-endpoint-test) に登録されている ID と一致する必要があります。

EP側: function の紐づけと controller の有効化

function と controller の紐づけを行います。<pciep> はコントローラのデバイス名で、ドライバが正しく登録できていればデバイス名のディレクトリがあります。

ln -s functions/pci_epf_test/HOGE controllers/<pciep>/

controller を有効にします。

echo 1 > controllers/<pciep>/start

RC 側: kernel 再構築 とツールのビルド

RC から EP へ送受信を行うテストドライバを有効にします。モジュールとしてビルドします。

CONFIG_PCI_ENDPOINT_TEST=m

さらに userland ツールの "pcitest" をビルドし、rootfs に置いておきます。

cd tools/pci; make pcitest

実機テスト(RC側)

RC 側のテストドライバを有効にします。

modprobe pci_endpoint_test

テストドライバは /dev/pci-endpoint-test.0 を作成し、"pcitest" ツールはこれを使って endpoint と通信します。テストではありますが、BAR や 使用メモリの設定は endpoint 側のテストドライバが行っており、RC 側ではコマンドを実行することで、PCI バスを介して endpoint にコマンドを投げ、その応答を割込みとして受け取って結果を返します。

pcitest -b 2   # BAR2 へのアクセスを確認
pcitest -m 1   # MSI1 を使って割込みを確認
pcitest -c     # endpoint へメモリをコピーし、さらにRCへメモリをコピーし確認

RC-EP 接続環境の問題点

RC-EP 接続を行う場合に注意すべき点があります。まず endpoint 側の Linux を起動して controller の有効化まで行いますが、RC 側の Linux が起動すると PCI host ドライバが初期化の時点でリセット(PERST#) を掛けてきます。これを endpoint が受けてしまうと、先の configuration がすっかり消えてしまいます。HW として消えるだけでドライバには設定が残っているため、unbind/bind をやり直せば復活はできるのですが、リセットを検知してやり直す機能は upstream kernel 上での実現には至っていません。

参考: https://patchwork.ozlabs.org/project/linux-pci/list/?series=226106

代わりに upstream kernel では core_init_notifier として定義されています。これは、endpoint が RC からのリセットを検知してから初期化を始めるというもので、nvidia:tegra で実現されています。残念ながら途中でリセットを掛けてくる RC と接続した場合はやはり消えてしまいます。まだこの部分については完成度が高くないようで、不具合も報告されています。


コメント

タイトルとURLをコピーしました