Linuxカーネルリリースにまだ到達しなかった1つのエラーの物語

最近では、Intelプロセッサで起動するときに32ビットLinuxカーネルが完全にハングアップすることを排除する修正プログラムがリリースされました。 ここに、エラーがどこから来たのか、そしてその発生の原因を見つけるためにどのような研究が行われたのかについての短い話があります。



読み込みプロセスについて少し説明します。 OSのロードには多くのフェーズがあり、そのうち2つはブートローダーのロード(トートロジーについてはごめんなさい)、OSのカーネル(この場合はLinux)のロードなど、すでに多くの人が知っています。 ブートローダーの制御をLinuxカーネルに移した直後に何が起こるか、少し掘り下げてみましょう。



従来、Linuxカーネルは、実行に応じて2つの部分(ブートと実行可能)に分割できます。 カーネル制御を受け取った後、ブート部分を実行します。これは、システムの物理メモリ内の圧縮解除とカーネルの場所を考慮します。 次に、メモリマネージャの最小構成があり、プロセッサの種類とそのフラグなどを検出します。 これらの手順の後、制御はコードに転送され、アーキテクチャに接続されていないカーネルの部分が直接動作を開始します(厳密には、これは完全に真実ではありませんが、ここではアセンブラコードからCコードへの移行を強調します)。 このプロセスについては、 [1]で詳しく説明しています。



ここで、現代のプロセッサがいわゆる 一部のプロセッサ命令の実行を構成する「マイクロコード」。 また、結晶を再放出せずに鉄の欠陥を除去することもできます。



OSのカーネル開発者の自然な欲求は、ブートプロセス中にできるだけ早くパッチを適用できることです。 以前は、Linuxでは、かなり遅い段階でロードを開始したユーザー空間の特別なデーモンがこの問題に対処していました。



数年前、Fenghua Yuはマイクロコードファイルを初期RAMディスクイメージ(initrd)に入れ、初期段階で使用することを提案しました( [2]を参照 )。 この変更により状況は大幅に改善されましたが、ファイル名が固定されているため、特に初期ディスクイメージが必要であり、プロセッサーの異なるバージョンのマイクロコードを保持できないという欠点がありました。



ごく最近、ボリスラフ・ペトコフは、最初の変更を変更することで修正することを決定しました[3]



これがダンスの始まりです。 64ビットおよび32ビットカーネルのload_ucode_bsp()



関数の呼び出しは、カーネルブートプロセスのさまざまな本質的に異なる場所から行われます。 64ビット環境では、MMUとプログラムメモリマネージャーが初期化されるCコードから呼び出しが既に行われていますが、32ビットの場合はずっと早く発生します。



この動作の効果はこれでした。 初期段階で実行される因果関数load_builtin_intel_microcode()を検討してください。



 static bool __init load_builtin_intel_microcode(struct cpio_data *cp) { unsigned int eax = 0x00000001, ebx, ecx = 0, edx; unsigned int family, model, stepping; char name[30]; native_cpuid(&eax, &ebx, &ecx, &edx); family = __x86_family(eax); model = x86_model(eax); stepping = eax & 0xf; sprintf(name, "intel-ucode/%02x-%02x-%02x", family, model, stepping); return get_builtin_firmware(cp, name); }
      
      





核内ライブラリ関数sprintf()



の呼び出しに注意してください。 システムを破壊するのは、パラメータに関係なく(その正確さに応じて)彼女の呼び出しです。



そこで何が起こっていますか? 私の同僚であるMIka Westerbergは、その理由は正確にはそのような初期のコード呼び出しにあると示唆しました。実際には、関数は仮想アドレスではなく物理アドレスで呼び出されます。 MMUが構成され、メモリマネージャーが初期化されるまで、仮想アドレスは機能しません。そのため、実行には、仮想アドレスと物理アドレス1-in-1の対応が必要です。 (ところで、 strcpy()



を呼び出そうとすると、結果は同じになります。)



マージウィンドウが近くに現れます(少し前に[4]で説明しました )、Borislavは32ビットカーネルの変更を一時的に無効にすることにしました[5]



f話の教訓は、OSのロードは非常にデリケートなプロセスであり、そこで何が起こっているのかを理解するには、アーキテクチャのかなり深い知識が必要だということです。



[1] www.ibm.com/developerworks/library/l-linuxboot

[2] lwn.net/Articles/530346

[3] www.spinics.net/lists/linux-tip-commits/msg28000.html

[4] habrahabr.ru/post/253421

[5] permalink.gmane.org/gmane.linux.kernel/1969480



更新

重要な発言を1つ加えるのを完全に忘れました。 多くの開発者は、同じQEMUを使用して、実際のマシンではなく仮想マシンでコードをテストします。 そこですべてがうまく動作します。



コメントの中で、 jcmvbkbcは何が起こっているのかという分析を共有しました。



All Articles