インフラ・DevOps
SystemIOの競合はファームウェアのバグではない
SystemIO conflicts are not firmware bugs (codon.org.uk)
要約
この記事は、ACPI(Advanced Configuration and Power Interface)におけるSystemIOの競合警告が、一般的にファームウェアのバグではなく、ACPIの仕組みとハードウェア抽象化の性質に起因することを解説しています。ACPIのOperationRegionとOSドライバー間の競合が発生するメカニズムと、その解決策について説明しています。
全文翻訳
私は全く関係のないことを調べていたのですが、検索結果で、ACPI Warning: SystemIO range 0x0000000000001828-0x000000000000182F conflicts with OpRegion 0x0000000000001800-0x000000000000187F のようなエラーがファームウェアのバグだとまだ多くの人が考えていることに気づきました。これは一般的に真実ではありません。なぜそうなのかを明確にするために、ACPIが何であるかを少し掘り下げる必要があります。Advanced Configuration and Power Interface1仕様は多くのものを定義していますが、ここで興味深いのはそれが実行するハードウェア抽象化です。PCは名目上はよく定義されたプラットフォームですが、ある程度の複雑さを超えると、ハードウェアレベルでは実際にはそうではありません。システムをサスペンドするときは、例えばハードウェアを正しい順序で電源オフにしたいと思いますが、その順序を知るには、特定のマザーボードデザインの詳細を知る必要があります。組み込みの世界で取られるアプローチは、その知識を何らかの形でOSに焼き込むことですが、それがDevicetreeに至る道です。ACPIは代替アプローチを取ります。その情報をOSドライバーが消費しなければならないデータとして提供するのではなく、コードとして配布します。ACPI Source Language、またはASLは、バイトコードにコンパイルされ、実行時にOSによって解釈されるシンプルな言語です。この言語の機能の1つは、「Operation Regions」を定義する機能です。これは実質的に、基盤となるハードウェアへのアクセスを記述する構造定義です。2つのレジスタが公開されているシンプルなデバイスを想像してみましょう。最初のものはインデックスレジスタです。これは、アクセスしたい内部レジスタのアドレスを指定します。2番目のものはデータレジスタで、これを読み取ると、インデックスレジスタに現在あるアドレスの内部レジスタの値が得られ、書き込むと、そのレジスタが変更されます。例としてのOperation Region宣言は次のようになります。
OperationRegion(OPR1, SystemIO, 0x400, 0x2)
Field(OPR1, ByteAcc, NoLock, Preserve) {
INDX, 8
DATA, 8
}
これは「OPR1」という名前のOperation RegionをIOポート0x400で、2バイトの長さで定義します。その中に、INDXとDATAという2つの8ビットフィールドがあります。これらは一度に1つずつアクセスされ、アクセス時にACPIインタープリターがグローバルロックを取る必要はなく、レジスタの一部が変更された場合でも他の値は保持されるべきです(フィールドが1バイト幅しかない場合はこのケースでは無関係です)。これで、このスコープ内のINDXまたはDATAへの参照は、それらのレジスタへのアクセスをトリガーします。したがって、レジスタ0x03の値を読み取るメソッドは次のようになります。
Method (RD03) {
INDX = 0x3
Return (DATA)
}
つまり、INDXを3に設定し、次にDATAの値を読み取って返します。しかし!もし他のACPIメソッドが同時に実行されていたらどうでしょうか?例えば、レジスタ0x05に書き込むメソッドがあるとします。
Method (WR05, 1) {
INDX = 0x05
DATA = Arg1
}
WR05の途中でRD03が実行されたらどうなるでしょうか?INDXは0x03にリセットされ、WR05は0x05ではなくレジスタ0x03を変更してしまうかもしれません。ああ、大変です!しかし、これを回避できます。ミューテックス(Mutex (MUTX, 0x00))を宣言し、メソッドを次のように更新します。
Mutex (MUTX, 0x00)
Method (RD03) {
Acquire (MUTX, 0xFFFF)
INDX = 0x3
Local0 = DATA
Release (MUTX)
Return (Local0)
}
Method (WR05, 1) {
Acquire (MUTX, 0xFFFF)
INDX = 0x05
DATA = Arg1
Release (MUTX)
}
各メソッドはロックを取得し(0xFFFFミリ秒まで待機し、失敗した場合はエラーを返します)、アクセスを実行します。これで競合する可能性はなくなりました。ふう!さて、誰かがこのハードウェアのLinuxドライバーを書いたとしましょう。それはACPIを知らずにハードウェアに直接アクセスします。ドライバーがACPIアクセスメソッドのいずれかと競合するのを止めるものは何でしょうか?何もありません。また、大変です!これは仮説ではありません。比較的無害な例ですが、昔は温度監視チップがファームウェアとLinuxによって同時にアクセスされ、その結果、温度を読み取っていると思ったら実際にはステータスフラグを読み取っており、ありえないほど高い温度と即時のサーマルシャットダウンにつながるケースに遭遇しました。この場合、カーネルはACPI Warning: SystemIO range 0x0000000000000400-0x000000000000401 conflicts with OpRegion 0x0000000000000400-0x0000000000000401 (OPR1) のようなメッセージを表示して、この(潜在的にハードウェアを損傷する)結果からあなたを救います。これは、カーネルがドライバーがIOポート0x400-0x401を割り当てようとしていることを検出したが、同じアドレスを主張するACPI Operation Region OPR1が存在することを意味します。カーネルは、ファームウェアがその領域でどのような種類のアクセスを行う可能性があるかを知る立場にないため、危険であると想定し、ドライバーのロードをブロックします。しかし、すべてが失われたわけではありません!カーネルはまた、役立つアドバイスも表示します。ACPI: このデバイス用のACPIドライバーが利用可能な場合は、ネイティブドライバーの代わりにそれを使用してください。そして、ACPIテーブルにはしばしば次のような定義が含まれています。
Device (HDW1) {
Name (_HID, "VEND0001")
OperationRegion(OPR1, SystemIO, 0x400, 0x2)
Field(OPR1, ByteAcc, NoLock, Preserve) {
INDX, 8
DATA, 8
}
Mutex (MUTX, 0)
Method (RD03) {
Acquire (MUTX, 0xFFFF)
INDX = 0x3
Local0 = DATA
Release (MUTX)
Return (Local0)
}
Method (WR05, 1) {
Acquire (MUTX, 0xFFFF)
INDX = 0x05
DATA = Arg1
Release (MUTX)
}
}
これはACPIデバイスとその関連メソッドを定義します。_HIDフィールドはデバイスタイプを定義し、Linuxドライバーは、タイプVEND0001のデバイスが見つかった場合に自動的にロードされるように書くことができます。そのドライバーは、デバイスに関連付けられたACPIメソッドを呼び出し、ファームウェアの期待に沿った方法でリソースにアクセスできます。(そのようなドライバーの作成に興味がありますか?私は2009年にガイドを書きました)
ファームウェアはここでは全く何も間違ったことはしていません2が、ネイティブドライバーをロードしようとするとエラーが発生し、インターネットはPCファームウェア開発者は無能である3と教えてくれ、この動作をオーバーライドするカーネル引数を渡すべきだと言われます。そして、それは彼らに害を及ぼすことはなく、あなたにも害を及ぼさないでしょうが、害を及ぼす可能性があり、システムが時々フリーズしたり発火したりする理由を決して知らないかもしれません。ACPI仕様は以前はacpi.infoにありましたが、残念ながらUEFIが仕様の管理を引き継いだ後、ある時点で消滅したようです。
ファームウェアは実行時に何もすべきではないと主張するかもしれません。それはファームウェアの仕事ではないからです。私もそれを理解しており、確かにacpi=offでブートすることはできます。そうすれば、実行時にACPIコードは一切実行されません。それがどうなるか教えてください。
ここではそれについての意見は述べませんが、その主張を裏付ける証拠は提供されないことを申し添えます。