Reverse Engineering x86 Processor Microcodeを読んだ

この頃、Zenbleed、Inception、Downfall などのプロセッサー脆弱性が話題になっている。 これらの脆弱性にはベンダーからの microcode アップデートで対応できると言われている。

はて、microcode とは何だったか。なんとなく雰囲気はわかっているものの勉強し直そうと思い適当に検索したら出てきた、Reverse Engineering x86 Processor Microcode という論文を読んでみることにした。

この論文は 2017 年に発表されていて、当時としては比較的モダンな AMD の k8/k10 マイクロアーキテクチャの microcode のリバースエンジニアリングを行って、 モダンな商用プロセッサの microcode がどのようなものでどのように実現されているかということを調べている。

まずはプロセッサだが、論理的には大きく2つの部分に分けられる。1つはレジスタファイルや ALU などを持つデータパスと、もう一つは、命令をデコードしデータパスのコントロールシグナルを発行するコントロールユニットだ。 コントロールユニットのデコーダはざっくり言えば命令からコントロールシグナルへのマッピングを行う。このようなマッピングは hardwired で実装することも可能だし、ROM 内に保存された表を引くことでも実装できる。 そして後者の実現方法を microcoded decode unit という。

microcode は大きく2つの構造があり、データパスのユニットのコントロールシグナルを直接発行する horizontal encoding と、データパスのユニットを駆動する RISC 命令のようなものを発行する vertical encoding というものがある。 vertical encoding による microcoded decode unit は ISA を microcode instruction に変換する一種のインタープリタとみなすことができる。 当然 microcode instruction は直接はデータパスを駆動できないので、実際のシグナルを生成するデコーダが必要になる。

AMD の k8/k10 マイクロアーキテクチャでは、vertical encoding の microcoded decode unit を採用しているらしい。 もちろん、すべての命令を microcode でデコードすると遅いので、レイテンシが求めれらるような単純な命令は hardwired なデコーダを使用する。 そのためモダンな x86 CPU ではハイブリッドなデコーダを採用していることになる。

AMD の microcode アップデートは ROM に書かれた microcode decoder の一部を書き換えることで行っている。 この ROM はレイテンシのため、トランジスタの配線の仕方によって実現されているらしく、物理的に書き換えることはできない。 そのため書き換えは ROM のパッチ先アドレスを格納する match register と RAM によって実装している。 つまり、microcode decoder の表引きを行うときに、アドレスが match register に合致するなら RAM を読み、そうでないなら ROM を読むことで書き換えを実現する。 microcode パッチは RAM に格納するので、起動時に BIOS や OS から毎回 CPU にパッチをロードする必要がある。

AMD の microcode アップデータは 2011 年まで暗号化されていなかったらしく(Intel は 1995 年から暗号化している)、筆者らは専用の OS を作成してアップデータを書き換えて挙動を見ることで、 どのような機能が microcode で実現されているか調べている。

microcode は 3オペランドRISC のような命令列で表現されているらしい。 書き換え可能な命令は、microcode で実現された命令に限られ、hardwired でデコードされる命令は書き換え方法がわからないということだった。

論文を読んでみて microcode アップデートによる修正がどのように実現されているか知ることができた。 Zenbleed は一種のロジックミスなので、microcode 更新によって実行される microcode instruction 数が増減しそうだがそれほどペナルティはなさそうだ。 一方で、Inception や Downfall ではキャッシュインバリデートやフェンスが必要になるのでペナルティが多いのだろう。