PCIの世界では、PCIのレガシー割込みは過去のものとなりました。PCI Expressではより効率的なMSI割込みが導入され、様々なアプリケーションで広く利用されています。しかし、これらは主に下位互換性と起動サポートのために依然として使用されています。
レガシー割込み[INTx]はPCIでは物理信号として扱われていましたが、PCIeデバイスには物理的なINTxピンがありません。そのため、PCIeはこれらのレガシー割込みをサポートするために、それらを論理的にエミュレートします。これは、仮想INTxメカニズムのようなものです。このガイドでは、開発者や愛好家が実践形式で動作を確認できるよう、これらの割込みがPCI Expressでどのように実現されているかを見ていきます。このブログのテーマは以下のとおりです。
1. PCIe のレガシー INTx スタイルの割込みについて考慮する必要があるのはなぜですか?
なぜPCIeのレガシーINTxスタイルの割込みにこだわる必要があるのですか?
今現在に至るまで、レガシー INTx割込みを理解することが重要な理由は以下のとおりです。
システム概要
Figure. 1図1
テスト対象のシステムには以下のコンポーネントがあります。
ハードウェア・コンポーネント:
iMX8MM EVKボード - i.MX 8Mミニ評価キット | NXP Semiconductors
iMX95 19x19 EVK - i.MX 95アプリケーション・プロセッサ・ファミリ | NXP Semiconductors
ソフトウェア・コンポーネント:
GitHub - nxp-imx/linux-imx: i.MX Linux kernel
iMX95 RC上のLinux BSP - 6.6.3
iMX8MM EP上のLinux BSP - 6.1.55
図1で示されている通り、システムではiMX8MM EVKとiMX95 19x19 EVKがM.2 Key E PCIeブリッジを介して並べて接続されています。
iMX8MM EVKはPCIeエンドポイントとなり、iMX95 EVKはPCIeルートコンプレックスとして機能します。
iMX95<------> iMX8MM
[RC] [EP]
上記の構成でセットアップするには、こちらのブログをご参照ください。
iMX95 torradexボードとiMX8MM EVK上でのPCIeエンドポイント・フレームワークの有効化 - NXPコミュニティ
この後、PCIエンドポイント・テストフレームワークを使用して、iMX8MMとiMX95 EVKの間でPCIeトランザクションをトリガーできるようになります。
このセットアップを使用して、PCIe EP[iMX8MM EVK]がPCIe RC[iMX95 EVK]にレガシーINTx割込みを送信する方法を示します。また、linux-imxソースコード上のLinuxドライバにおけるこれらの割込みの発生源も追跡します。
INTx レガシー割り込みは PCIe でどのように実現されますか?
PCI Expressは、PCIローカルバス仕様で定義されているPCI割込みをサポートします。これには、PCIコンフィグレーション・スペースの割込みピン・レジスタと割込みライン・レジスタが含まれます。PCIeデバイスは下位互換性を確保するため、これらのレジスタをサポートします。したがって、実際のシグナル割込みは物理ピンを使用するのではなく、インバンドメッセージを使用します。
PCIeでは、仕様に従って2種類のメッセージが定義されています。
PCI INTxシグナルのエミュレーションのためのAssert_INTxとDeassert_INTx
x -> A/B/C/D
これらのメッセージはリンク間でのシグナル割込みの「仮想ワイヤー」として機能します。これら「仮想ワイヤー」の形式のメッセージは「ルート・コンプレックス」に到達し、その後システム割込みコントローラにマッピングされます。つまり、RCはこれらインバンドの「仮想ワイヤー」メッセージを受信すると、それを適切なハードウェア割込みに変換してCPUに送ります。
注:PCI Express IntXエミュレーションのシステム割込みへのマッピングは、実装によって異なります。物理的なシグナル割込みと同様に、INTxエミュレーション・メカニズムは疑似割込みを引き起こす可能性があります。これはシステム・ソフトウェアによって処理する必要があります。
iMX8MMでのINTxエミュレーションの有効化とテスト
公式のLinuxファクトリーカーネルでは、レガシー割込みはまだサポートされていません。このブログには、PCIeのレガシー割込みを有効にするためのパッチを含むZIPファイルが添付されています。以下の3つのファイルがあります。
1.legacy_intx_tested_imx8mm_patch_1
パッチの適用方法は、READMEファイルに記載されています。何か問題がありましたら、お気軽にDMを送信してください。
LinuxでPCIe INTxエミュレーションを有効にする手順を簡潔にまとめると、以下の通りです。
1. ボード上でデフォルトのiMX8MM 6.1.55 wicイメージをフラッシュし、Linuxで起動することを確認します。
2. linux-imxリポジトリを取得[ブランチlf-6.1.yをチェックアウト]します。
3. Linuxのソースコードにパッチを適用します。
4.カーネルとscp「arch/arm64/boot/Image」をボードにビルドします。この「Image」と「imx8mm-evk-pcie-ep.dtb」でボードを起動します。
問題が発生した場合は、次のブログを参考にしてください:iMX95 torradexボードとiMX8MM EVK上でのPCIeエンドポイント・フレームワークの有効化
この時点で、PCIe EP iMX8MM上で動作するカーネルは、PCIe RC [iMX95]にINTx割込みを送信できます。テスト方法は以下のとおりです。
1. iMX95 RCがM.2経由でiMX8MM EPに接続されていることを確認します。どちらも起動しており、iMX8MMはiMX95のPCIeエンドポイントとして表示されます。iMX95で「lspci」を実行すると、「割り当てられていないクラス」というエントリが表示されます。
2. iMX95EVK RC上で、以下の2つのコマンドを実行して、INTxエミュレーションをテストします。
a. pcitest -i 0
b.pcitest -l
コンソール出力:-
root@imx95evk:~# pcitest -i 0
IRQタイプをレガシーに設定:OK
root@imx95evk:~# pcitest -l
レガシーIRQ:OK
「pcitest・i 0」は、レガシーINTx割込みを処理するようにRCを設定します。この結果、ioctlコールは「PCITEST_SET_IRQTYPE」となります。
drivers/misc/pci_endpoint_test.cでは以下のように処理されます。
Figure. 2図2
IRQ_TYPE_LEGACYのargは0です。
以下のスニペットでは、すべてがどこから始まるのかを示しています。
Figure. 3図3
pci_endpoint_test_release_irqの内部
Figure. 4図4
test->num_irqs=16の場合、エンドポイントでは以下のようになります。
echo 16 > functions/pci_epf_test/func1/msi_interrupts
pci_irq_vectorは、MSI割込み0から16に対応するLinux IRQ番号を返します。
その後、devm_free_irqを使ってこれらのirqを解放し、再び割り当てられるようにします。
free_irqはこの関数内で使用され、割込みハンドラを削除します。
そして、割り込みラインがどのドライバによっても使用されていない場合、それは無効化されます。
共有IRQでは、呼び出し元はこの関数を呼び出す前に、駆動するカード上で割込みが無効になっていることを確認する必要があります。
pci_endpoint_test_free_irq_vectorsの内部
pci_free_irq_vectors[driver/pci/msi/api.c]を呼び出し、以下を行います。
pci_alloc_irq_vectors_affinity()またはpci_alloc_irq_vectors()によって以前に行われた割込みベクタの割り当てとデバイスMSI/MSI-Xのイネーブルメントを元に戻します。
注:PCIエンドポイント・テストドライバでは、IRQベクタを割り当て、レガシーINTx割込みを要求する前に、既に割り当てられたMSI割込みを解放し、それに関連付けられたベクタを解放する必要があります。その過程で、MSI割込みは無効化されます。これはPCIeの仕様上、MSIとINTxが互いに排他的であるためです。MSIとINTxの両方を有効にするとどうなるかテストしてみると面白いでしょう。テストの結果何が起こったかをコメント欄から教えてください。
pci_endpoint_test_alloc_irq_vectorsの内部
Figure. 5図5
pci_alloc_irq_vectorsは、従来のintx割込みに割込みベクタを割り当てます。これにより、内部深くに、CPUコア0のCPUアフィニティ・マスクが作成され、pci_intxが呼び出され、PCIe構成スペースの読み取りと書き込みによってレガシー割込みが有効になります。
Figure. 6図6
pci_endpoint_test_request_irqの内部Figure. 7図7
pci_irq_vectorは、
devm_request_irq -> devm_request_threaded_irq
に渡されるLinux IRQ番号を割り当てます。
Linuxカーネルのdevm_request_irqはデバイス管理型リソース割り当てAPIの一部です。このAPIの主な役割は、IRQリソースのライフサイクルを自動的に管理することで、デバイス・ドライバの割り込みリクエスト処理を簡素化することです。また、IRQ番号の割込みハンドラを登録し、IRQリソースをデバイスのライフサイクルに結びつけます。デバイスが切り離されるか、ドライバがアンロードされると、カーネルは自動的にIRQを開放します。
IRQF_SHAREDフラグを持つハンドラ「pci_endpoint_test_irqhandler」が渡されます。このハンドラはINTx割込みを着信したときに呼び出されます。
IRQF_SHARED -- 割込み回線を複数のデバイスと共有できます。
注:Linux IRQサブシステムについてはこのブログの範囲外です。カーネルAPIの一部に戸惑っても、今後のブログで取り上げる予定ですのでご安心ください。
ここまでで、PCIeでINTxを有効にすると裏で何が起こるかが少しわかったと思います。
「pcitest -l」がトリガーするIRQテストでは、すべてがうまくいった場合「OKAY」を、そうでない場合は「NOT OKAY」を返します。
上記のテストを実行すると、PCIe RCはpcie link上のiMX8MM EPにコマンド「COMMAND_RAISE_LEGACY_IRQ」を送信します。このコマンドをハンドラ「pci_epf_test_cmd_handler」の一部として受け取ったEPは「pci_epf_test_raise_irq」を呼び出します。
Figure. 8図8
Figure. 9図9
pci_epc_raise_irqは、最終的にパッチの__dw_pcie_ep_raise_intx_irqを呼び出します。Figure. 10図10
これはPCI_CODE_ASSERT_INTAメッセージを送信し、その後遅延を挟んでからPCI_CODE_DEASSERT_INTAを送信します。これらのメッセージはどちらもPCI_MSG_ROUTING_LOCALというルーティングタイプで、同じデバイス内の特定の機能またはスイッチにルーティングされることを意味します。メモリトランザクションがターゲットアドレスを必要とするのとは異なり、INTxメッセージには宛先アドレスがありません。
簡単に言うと、「これを私のローカルルートコンプレックスにルーティングしてください」ということです。
Figure. 11図11
その後、ルート・コンプレックスiMX95に到達し、そこから、共有ペリフェラル優先割込みを使用して、割込みコントローラ[GICv3]に到達します。GICは、割込みを処理するよう、CPUコアのいずれかに通知します。ここでは、アフィニティをCPU 0に設定しているため、CPU 0がこれを処理します。最後に、RCのエンドポイント・テストフレームワークでpci_endpoint_test_irqhandlerが呼び出されます。Figure. 12図12
割込みがGICに到達した後のシーケンスは以下のとおりです。
Figure. 13図13
JTAGをiMX95EVKボードに接続すると、LinuxでレガシーINTx IRQが処理される際に呼び出しを追跡できます。
Figure. 14図14
imx95上のGICにルーティングされた割込みは、「pcitest -l」を使用してレガシー割込みテストをトリガーするたびに、
「cat /proc/interrupts」
を実行することで確認できます。
INTAの場合、「cat /proc/interrupts」の出力に次のように表示されます。Figure. 15図15
[割込みを処理した]CPU0に表示されている数字の「3」は、レガシー割込みテストをトリガーするたびに増加することがわかります。これは、CPUコアのIRQ番号に対していくつの割込みが生成されたかを示します。
レベル:割込みタイプ
「224」は割込み要求番号です。
//INTA、INTB、INTC、INTDのテスト
単一機能デバイスでサポート可能なINTx割込みの数は1つのみです[PCIeの場合はエミュレートされます]
INTB、INTC、またはINTDを使用するには
__dw_pcie_ep_raise_intx_irq関数の第3引数
0 = INTA
1 = INTB
2 = INTC
3 = INTD
上記のマッピングに従って変更します。
test_headerのinterrupt_pinメンバを変更できます。
PCI_INTERRUPT_INTA = INTA
PCI_INTERRUPT_INTB = INTB
PCI_INTERRUPT_INTC = INTC
PCI_INTERRUPT_INTD = INTD
-- INTAの割込みのモニタリング
Figure. 18図18
-- INTBの割込みのモニタリング
Figure. 19図19
-- INTCの割込みのモニタリング
Figure. 20図。20
-- INTDの割込みのモニタリング
Figure. 21図21
上記のスニペットから、コア0のみがIntX IRQを処理していることがわかったはずです。これは、CPUアフィニティ・マスクが「1」に設定されることで、CPUコアが「0」になるためです。
kernel/irq/affinity.cの内部
Figure. 22図22
レガシー割込みのiRQマスクは「1」に設定されているため、コアは「0」になります。
このブログはここで終了です。細かいところまで詳述したため、かなり長くなりました。情報の多さに圧倒されそうでも大丈夫。PCIEとIRQ Linuxサブシステムでの作業を始めれば、次第に理解できるでしょう。この件についてご質問があればお知らせください。DMでもメールでもお気軽にどうぞ。また次回お会いしましょう。
PCIの世界では、PCIのレガシー割り込みは過去のものとなりました。PCI Expressではより効率的なMSI割り込みが導入され、様々なアプリケーションで広く利用されています。しかし、これらは主に下位互換性と起動サポートのために依然として使用されています。
レガシー割り込み[INTx]はPCIでは物理信号として扱われていましたが、PCIeデバイスには物理的なINTxピンがありません。そのため、PCIeはこれらのレガシー割り込みをサポートするために、それらを論理的にエミュレートします。これは、仮想INTxメカニズムのようなものです。このガイドでは、開発者や愛好家が実践形式で動作を確認できるよう、これらの割り込みがPCI Expressでどのように実現されているかを見ていきます。このブログのテーマは以下のとおりです。
1. PCIe のレガシー INTx スタイルの割り込みについて考慮する必要があるのはなぜですか?
2. システム概要
3. PCIeでINTxレガシー割り込みはどのように実現されるのか?
4. iMX8MMでのINTxエミュレーションの有効化とテスト