読み込みプロセスについて少し説明します。 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は何が起こっているのかという分析を共有しました。