80386マイクロコードが逆アセンブルされる
(reenigne.org)- 80386マイクロコードROMは94,720ビットで、8086の10,752ビットよりはるかに大きく、画像変換と検証が難しかった
- 高解像度のダイ画像から、画像処理・ニューラルネットワーク・人手支援の自動化を組み合わせて、数日でバイナリブロブを抽出し、相互検証した
- 逆アセンブルの過程で、μ-op配列、ビットフィールド、命令終了パターン、命令デコーダと保護テストPLAの構造が徐々に明らかになった
- 80386ではすべての命令に対応するマイクロコードがあり、多くのルーチンはアルゴリズムそのものより、乗算・除算ハードウェアやバレルシフタの設定を担っている
- 保護モードのIO権限ビットマップ処理では、4バイトのポートアクセス時に最初の3つのアドレスだけを検査しているように見える潜在的不具合が見つかったが、まだ確定ではない
80386マイクロコードの抽出と逆アセンブル
- 8086マイクロコード逆アセンブル の後、Ken Shirriff が80386のマイクロコードROMの高解像度画像を提供したが、80386のROMは94,720ビットで、8086の10,752ビットよりはるかに大きく、変換と検証はずっと困難だった
- 8086には全体構造と一部のコード断片を含む特許があり、探索の手がかりがあったが、80386は完全なブラックボックスに近く、大きなバイナリの塊の中から構造を見つけるのが難しかった
- Discordでは GloriousCow や Smartest Blob らが80386ダイの高解像度画像からマイクロコードを抽出する作業を進めており、画像→バイナリ→理解可能なマイクロコードへ変換する過程が中核的な難所だった
- 画像処理、ニューラルネットワーク、人手支援の自動化を組み合わせ、数日で画像からバイナリブロブを抽出し、相互検証した
逆アセンブルの過程で見えてきた構造
- バイナリ抽出後も逆アセンブルは容易ではなく、さまざまなパターンを突き合わせながら、一方の軸にμ-op、もう一方の軸にμ-opビットが来るよう再配置されていることを突き止めた
- 未使用のμ-opブロックが片端にあり、それがμ-opを読む順序を把握する手がかりになった
- μ-opビットを複数のフィールドに分割する過程では、8086マイクロコード解析の経験をもとに、ソースレジスタと宛先レジスタのフィールドを見つけていった
- 80386ではALU演算を2サイクルで実行できるため、1サイクル目で2つのオペランドをALUにロードし、2サイクル目で結果を宛先へ送るには、ALUの第2入力を指定するフィールドが必要だった
- 繰り返し現れるパターンは命令の終わりを示すものと推定され、その後、実際に正しいことが確認された
- Kenは80386ダイ上の複数の配線と論理ビットを追跡し、内部接続の仕組みを把握することに貢献した。新たに判明した構造は、同じ構成要素を使う別のマイクロコード断片を解釈する手がかりにもなった
- マイクロコードに加え、複数の小さなPLAで構成される命令デコーダと保護テストPLAも解読され、386命令をマイクロコード断片に対応付けられるようになった
8086と異なる80386の実行方式
- 80386はほとんどの命令で8086よりサイクル当たり大幅に高速で、そのためにより多くのトランジスタを使っている
- 8086でマイクロコード実装されていた複数のアルゴリズムは、80386では事実上ハードウェアアクセラレータが担っている
- 80386マイクロコードのかなりの部分は、アルゴリズム自体よりも、乗算・除算ハードウェア、バレルシフタ、保護テストユニットといったアクセラレータを設定する役割を持つ
- 逆アセンブル作業の大きな比重は、こうしたアクセラレータインターフェースとマイクロコードの接続方式を把握することにあった
マイクロコードのエントリと命令処理
- デコードROMから入るマイクロコードのエントリポイントは215個で、8086の60個より大幅に増えていた
- エントリ増加の理由は新命令の追加だけでなく、オペランドがレジスタかメモリか、CPUがリアルモードか保護モードか、REPプレフィックスを使用中かによって、同じ命令でも異なるルーチンで処理されるためでもある
- すべてのエントリ一覧は
fields.txtにあり、サブルーチンや共有コードも含まれている - 多くの上位マイクロコードルーチンはごく少ない処理だけを行った後、他のエントリポイントと共有するルーチンへジャンプするため、上位ルーチンのサイズだけでは意味を把握しにくい
- 命令デコーダは使用するルーチンを決める際にopcodeだけを使うわけではないため、各エントリポイントが処理するopcode数だけでも構造を説明しにくい
すべての命令はマイクロコードで処理される
- 80386は8086や現代CPUと異なり、常にμ-opを実行しており、すべての命令に対応するマイクロコードが存在する
- マイクロコードで処理されない命令は存在しないことが確認された
未使用コードと例外処理の痕跡
0x849から0x856までのルーチンは、マイクロコード逆アセンブルではunused?と表示されており、接続されたエントリポイントがないように見える- このルーチンの正確な動作は完全には確定していないが、
0x8e9から0x8f5までの#PF(PAGE_FAULT) ルーチンと多くの共通点がある - 両ルーチンとも paging unit の最後のエラーコードを設定した後、interrupt 0x0e へ進む
- 違いは、このルーチンでは fault linear address の代わりに、paging unit から出た正体不明の値を
CR2に設定する点にある - それ以外のマイクロコードは、CPUの文書化された動作を実装するよう設計されているようで、低レベルデバッグ用の**ICE(In-Circuit Emulator)**ハードウェアと相互作用するルーチンは、文書化されていない動作に該当する
隠された機能と考えられるIO権限ビットマップ不具合
- まだ実機の386マシンで試験できていないため確定はできないが、保護モードOSの一部がユーザーモードプロセスにIOポートアクセスを限定的に許可する際に使っていたIO権限ビットマップ処理に、不具合の可能性がある
- 4バイトのポートアクセスが発生したとき、マイクロコードは最初の3つのアドレスの権限ビットしか検査していないように見える
- プロセスが権限を持つIOポート空間の境界でこのようなアクセスを行うと、最後の1バイトのアクセスが誤って成功し、OSがユーザーアクセスを意図していないハードウェアレジスタに到達する可能性がある
- このバグは非常に特殊な条件でしか起きず、マイクロコード逆アセンブルがなければ見落とされていた可能性があるが、40年以上広く使われたハードウェアのセキュリティバグが未発見だったというのは異例だ
- この挙動は一部のCPUバージョンにだけ存在した可能性もあり、あるいはルーチンの解釈が誤っていて、実際には正しく動作している可能性もある
- このマイクロコードは80386初期版のものではないようで、
XBTSとIBTS命令はデコーダを除けば痕跡がない
学習資料とダウンロード先
- nand2mario の80386内部構造に関する記事は、逆アセンブルを理解する出発点として有用だ
- 80386 Multiplication and Division
- 80386 Barrel shifter
- 80386 Protection
- 80386 Memory Pipeline
- 逆アセンブル結果は GitHubの x86 microcode リポジトリ から入手できる
parts.txtはほかのファイルの役割を案内しており、microcode_10.txtはマイクロコード逆アセンブルそのものに直接入るファイルである
1件のコメント
Hacker Newsのコメント
高解像度のダイ画像から、どうやってマイクロコードを復元できるのか気になる
各トランジスタを認識して回路をモデリングするのか、結果がVerilogのような形になるのかも知りたい
その後で0と1を分類したが、1はトランジスタの存在とポリシリコンの隙間によって視覚的に判別できた
Intelのマイクロコードの性質上、0の方がずっと多いと仮定できるので、トランジスタがあれば1と見なした
色のしきい値で自動処理するツールもあるが、モザイクの一部がぼやけていたり埃が入っていたりして、偽の1ビットが大量に発生し、うまく合わなかった
その代わり、畳み込みニューラルネットワークに抽出したビット領域を0/1に分類させるよう学習させ、結果を元のモザイクの上に白黒の長方形を50%の不透明度で重ねて確認した
その後、数日かけて地道に誤りを検査し、最終的に生の2次元ビット配列を得た。次の段階は、その配列からマイクロコード語を抽出することだ
https://youtu.be/HwEdqAb2l50?si=VFLed64PZvpCHfy1
「上の写真はマイクロコードROMの一部を示している。顕微鏡で見るとマイクロコードROMの内容が見え、各位置にトランジスタがあるかないかによってビットを読み取れる」
https://www.righto.com/2020/06/a-look-at-die-of-8086-process...
関連する進行中のスレッド: z386: An Open-Source 80386 Built Around Original Microcode - https://news.ycombinator.com/item?id=48248014 - 2026年5月、コメント22件
数日前にreenigneのブログを見たときは「うーん、2020年以降は記事がないな」と思ったが、また更新されていてうれしい
ブログが33年前までさかのぼるのも特に面白い
マイクロプログラミングを基礎から説明する良書: https://www.amazon.com/Computation-Structures-Optical-Electr...
無料PDFも簡単に見つかる
このマイクロコードをリバースエンジニアリングするのに必要な労力は印象的で、386アーキテクチャを深く掘り下げた素晴らしい記事だ
実際のマイクロコード実装を見ると、古いプロセッサが複雑な演算をどう処理していたのかが以前ほど神秘的ではなく感じられる
386は22年の生産期間中に小さな変更が多かったので、このコードがどの386リビジョン由来なのかを知ることが重要だ
9B5 BIST1 -> TMPD 0x0303 PASS29B6 SIGMA -> EDX9B7 BIST2 -> TMPE TMPD XOR9B8 SIGMA 0x3ddc0c2c XOR9B9 SIGMA -> EAX BOOTUP_JUMP JFPUOK0x303はファミリー3、モデル0、ステッピングID 3を意味するこれを解読するために必要なブラックボックス解析はとてつもなく難しいが、成功すれば非常に面白く、達成感も大きそうだ
こういう記事を理解するために大学で難しい科目を履修していたことに満足しているし、2015年当時のHNがそういう思考を刺激してくれたのもよかった
今では低レベルプログラミングの知識をあまり使わなくても、こうした記事を読むたびに意識が豊かになるように感じられて素晴らしい
大学にアクセスしにくい人にはnand2tetris.orgを勧めたい
単純な昔の設計であるRISCやTransputerを学ぶのも役に立つし、80386はそのスペクトラムの反対側にある
古い悪い設計や下位互換性を維持しようとして、不要に複雑になっているからだ
チップ設計を学ぶのに大学が必須というわけではなく、Alan Kayの講演をいくつか見たり、Bitsaversのコンピュータ設計資料をざっと見たりするのもよい出発点になる
FPGAより簡単な方法でゲートレベル設計をシミュレーションし、2026年時点で200ドル未満でチップ上のトランジスタへ変換するMorphle Logicを作った
最終的には、より大きく、より速く、より安価なウェハースケール・スーパーコンピュータ統合につながる可能性がある
https://github.com/fiberhood/MorphleLogic/blob/main/README_M...
https://www.youtube.com/watch?v=vbqKClBwFwI
https://www.youtube.com/watch?v=f1605Zmwek8
http://bitsavers.informatik.uni-stuttgart.de/pdf/xerox/alto/...
その単純さ自体は素晴らしい教訓で、多くの刺激を与えてくれたが、1990年代に大学で受けた電気工学の授業はnand2tetrisに似ていながら、8086系CPUがどう作られるかを扱い、さらにマイクロコードの動作も説明していた
内部プログラムカウンタが制御語テーブルをたどり、各ビットがCPUの制御可能な部分を直接調整する仕組みだった
各自がシミュレータで1つの命令を実装したが、私はDEC、つまりデクリメント命令を担当した
ある意味では、nand2tetrisの命令はマイクロコードだと見なすこともできる
命令ビットがハードウェアを直接制御し、最初のビットが2種類の命令を選ぶので、1命令あたりコード段階は1つしかない
一方でマイクロコードでは、1つの命令が任意の数のマイクロコード段階を持てる
Ben Eaterのブレッドボード8ビットCPUの動画では、命令の4ビット演算コードとステップカウンタでROMをインデックスし、制御語を決めている
このROMは、十分に複雑な論理ゲートでも作れる部分を置き換えており、電子回路に直接触れて問題を解決する必要があるので、ハードウェア側の次のステップとしてよい
ただしRAMが16バイトしかないため、nand2tetrisのようにより高い抽象化階層を作るのが難しいのは残念だ
その時点では、よりよい設計で作り直すか、PCBに載せるか、6502プロジェクトへ進んでタイマー、CPU、ROM、RAM、入出力、UARTなどを一体として考え、その後ですでにそれらが統合されているマイクロコントローラへ進むこともできる
論理ゲートでCPUを作る方法を読みたいなら、Charles PetzoldのCodeはゆっくり説明していて最近改訂されており、Danny HillisのPattern on the Stoneはもっと速いペースで進む
Code第2版では4ビットのサイクルカウンタとハードワイヤードな論理ゲートで各サイクル動作を決め、一部の論理にダイオードアレイを使っているが、これもマイクロコードと見なすべきなのか気になる