BIOSとマイクロコード

BIOSとマイクロコード
本稿執筆時に使っているマザーボードAsustekのP4B533-Eです。845E(Brookdale-E)チップセットの古いマザーです。最初に使ったCPUはCeleron1.7GHz(FSB:400MHz, S-Spec:失念, Wilametteコア)で、1年後にCeleron2.4GHz(FSB:400MHz, S-Spec:SL6XG(C1-Stepping), Northwoodコア)に交換し、これからPentium4-3.06GHz(FSB:533MHz, S-Spec:SL6PG(D1-Stepping), Northwoodコア)に交換します。


ASUSのWebサイトを調べると、1011以降のBIOSであればPentium4-3.06GHzに対応していることになっています。今入っているBIOSは1014なので、対応していることになります。でも1014のリリース日は2003年3月になっており、果たしてD1ステッピングのPentium4-3.06GHzに対応しているのかどうかはWebサイトを調べただけではわかりません。


本題に入る前に、CPUのステッピングとBIOSの対応を調べることがなぜ大事なのかを説明します。かつて初期のPentium(60MHz/66MHz/90MHz/100MHz)が、FPU命令のバグ(FDIV, FPREM, FPTAN, FPATAN)でリコール(交換・回収)されたことがありました。新聞で大々的に報じられ、IBMなどの大手メーカーが搭載機の出荷を自粛するなど大騒ぎになりました。(インテルが「トラブルに遭遇する確率は数千年に一回であり、実使用上まったく問題ない」と発表しているにもかかわらず出荷停止措置になるのを見ていて、メーカー側がトラブルに便乗してインテルをいじめているかのような印象を持ちました。)その後、インテルはこれに懲りたのか、CPUのマイクロコードにパッチを当てる仕組みを開発し、PentiumIIの頃から実装しています。(「PentiumIIの頃」という曖昧な表現になっているのは、どの製品から導入したのかが正確にはわからないためです。MMX Pentiumの頃からあるのかもしれません。)


以前「日経バイト」で読んだ記事によると、マイクロコードにパッチを当てる仕組みは、

  • BIOS内部にパッチを保持
  • CPU起動時にステッピングを読み取り、対応したパッチをCPUへ転送
  • パッチそのものは暗号化されている


のようなものだそうです。(この記事を失念してしまった。残念。)パッチがステッピング毎に用意されているのは、CPUのバグはステッピング毎に管理されているからです。エラッタについては、インテルが発行しているエラッタ集に記載されています。(CPUのエラッタが公開されるようになったのはPentiumのFDIVバグ騒動のもたらしてくれた思いがけない(いい)副産物だと思います。これ以前、エラッタ情報はエンドユーザーには非公開でしたから。)


BIOS内部でステッピングを読み取り、パッチを当てているので、CPUとBIOSの対応を調べることが重要になってくるわけです。なおインテル製CPUのステッピングは、Processor Spec Finderで調べられます。


BIOSを調べる
P4B533-EのBIOSはAward製なので、AwardのBIOSを編集・閲覧できるツールが必要になります。以前はAward社のWebサイトで配布されていたらしいですが、その後中止されたらしく、検索して探し出す必要があります。有名なのはCBROMで、オープンソースのものとしてAwardModがあります。もっと検索していたところ、うるりの物置きで‘MicrocodeViewerを見つけたのでこれを使うことにします。


Pentium4-3.06GHzに対応した最初のBIOSである1011を調べてみます。

【P4B533-E用BIOS(1011)の対応CPU】
<< C:¥BIOS¥1011E.awd >>
 < 1011E.awd/cpucode.exe >
2002/01/11 Rev. 0x12 (CPUID 0xF0A) ? PGA-603 Pentium 4 Xeon Processor
2002/01/12 Rev. 0x28 (CPUID 0xF12) SECC Unknown CPU
2002/02/11 Rev. 0x03 (CPUID 0xF13) SECC Unknown CPU
2001/05/29 Rev. 0x01 (CPUID 0xF21) SECC Unknown CPU
2001/07/30 Rev. 0x08 (CPUID 0xF23) SECC Unknown CPU
2002/01/14 Rev. 0x0B (CPUID 0xF24) SECC Unknown CPU
2002/06/28 Rev. 0x24 (CPUID 0xF27) SECC Unknown CPU


こりゃ、駄目です。D1-Stepping(CPUIDは0xF29)用のパッチが入っていません。次に今入っている1014を調べてみます。

【P4B533-E用BIOS(1014)の対応CPU】
<< C:¥BIOS¥1014e.bin >>
 < 1014e.bin/cpucode.exe >
2002/07/16 Rev. 0x14 (CPUID 0xF0A) ? PGA-603 Pentium 4 Xeon Processor
2002/07/16 Rev. 0x2C (CPUID 0xF12) SECC Unknown CPU
2002/07/17 Rev. 0x04 (CPUID 0xF13) SECC Unknown CPU
2001/05/29 Rev. 0x01 (CPUID 0xF21) SECC Unknown CPU
2001/07/30 Rev. 0x08 (CPUID 0xF23) SECC Unknown CPU
2002/07/19 Rev. 0x0F (CPUID 0xF24) SECC Unknown CPU
2002/11/06 Rev. 0x33 (CPUID 0xF27) SECC Unknown CPU
2002/11/11 Rev. 0x0A (CPUID 0xF29) SECC Unknown CPU


CPUIDが0xF29用のパッチが入っています。ついでにベータ版のBIOS(1015.003)を調べます。このBIOSはD1-Stepping以降しか存在していないCeleron2.8GHz(FSB:400MHz, Northwoodコア, Socket478)に対応しています。

【P4B533-E用BIOS(1015.003)の対応CPU】
<< C:¥BIOS¥1015003.bin >>
 < 1015003.bin/cpucode.exe >
2002/07/16 Rev. 0x14 (CPUID 0xF0A) ? PGA-603 Pentium 4 Xeon Processor
2002/07/16 Rev. 0x2C (CPUID 0xF12) SECC Unknown CPU
2002/07/17 Rev. 0x04 (CPUID 0xF13) SECC Unknown CPU
2001/05/29 Rev. 0x01 (CPUID 0xF21) SECC Unknown CPU
2001/07/30 Rev. 0x08 (CPUID 0xF23) SECC Unknown CPU
2002/11/07 Rev. 0x18 (CPUID 0xF24) SECC Unknown CPU
2002/11/06 Rev. 0x33 (CPUID 0xF27) SECC Unknown CPU
2003/04/14 Rev. 0x11 (CPUID 0xF29) SECC Unknown CPU


1014よりも新しいRevisionのパッチが入っています。BIOSをアップデートして1015.003に入れ替えようかと考えましたが、

  • 1014自体はPentium4-3.06GHzに対応したBIOSである
  • ベータ版のBIOSを使うのは精神衛生上よろしくない


などの理由から、1014のままで使い続けることにしました。


CPUを交換して電源を入れたところ、以下のような画面が表示され、正常に認識されました。Hyper Threading対応のCPUなので、CPU2の表示が出るようになっています。

起動時画面
Award Medallion BIOS v6.0, An Energy Star Ally
Copyright (C) 1984-2002, Award Software, Inc.

ASUS P4B533-E ACPI BIOS Revision 1014

CPU1:*Intel(R) Pentium(R) 4 3066 MHz Processor
CPU2: Intel(R) Pentium(R) 4 3066 MHz Processor
Memory Test :  1048576K OK


AwardのBIOSであっても最近のものや、AMIなど他社のBIOSでも上記のようにして調べられるのかどうかはわかりません。


■マイクロコードにパッチをあてる手順
BIOSが持っているCPUID:0F29h用マイクロコードのパッチのRevisionは、0x0Aですが、Windows XP SP2上でwcpuid, CrystalCPUIDを使って調べると0x21になっています。Windowsがパッチをあてているのだろうかと考えて調べてみたところ、マイクロコードにパッチをあてる方法が、インテルが発行しているIA32ソフトウェア・デベロッパーズ・マニュアルの「システム・プログラミング・ガイド」に書いてありました。(手元の版だと、「第8章:プロセッサの管理と初期化」の「8.11 マイクロコード・アップデート機能」)概要はこんな感じです。

  • アップデート用のデータのヘッダーは48バイト
  • アップデート用のデータは暗号化されており、サイズは2000バイトもしくは4バイト単位のサイズ
  • CPUはリアルモード・プロテクトモードのどちらでもよい
  • パッチデータは現在のCPUモードでアクセスできるアドレス空間内のどこに配置してもよい
  • リアルモードでパッチをあてる場合はセグメント境界をまたがないようにパッチデータを配置する
  • EDX:EAXにパッチデータのアドレスを入れ, ECXでMSRを指定した上で、WRMSRを使って書き込む
  • Pentium4の場合、MSRは0x79となる(ECX=79h)


Linuxでの実例


Linuxではカーネル経由でパッチをあてることができます。


さらにLinuxカーネル(2.6.8)のソースを調べてみたところ、ありました。パッチデータを読み込み、検証した上で、WRMSRを使って書き込んでいます。以下に引用します。


The Linux Kernel Archives

【Linuxのカーネル(2.6.8)のソース(i386/kernel/microcode.c)からの引用】
 *
 *  Copyright (C) 2000-2004 Tigran Aivazian
 *
 *  This driver allows to upgrade microcode on Intel processors
 *  belonging to IA-32 family - PentiumPro, Pentium II, 
 *  Pentium III, Xeon, Pentium 4, etc.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version
 *  2 of the License, or (at your option) any later version.
 *

static void do_update_one (void * unused)
{
    unsigned long flags;
    unsigned int val[2];
    int cpu_num = smp_processor_id();
    struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;

    if (uci->mc == NULL) {
        printk(KERN_INFO "microcode: No suitable data for CPU%d\n", cpu_num);
        return;
    }

    /* serialize access to the physical write to MSR 0x79 */
    spin_lock_irqsave(&microcode_update_lock, flags);          

    /* write microcode via MSR 0x79 */
    wrmsr(MSR_IA32_UCODE_WRITE,
        (unsigned long) uci->mc->bits, 
        (unsigned long) uci->mc->bits >> 16 >> 16);
    wrmsr(MSR_IA32_UCODE_REV, 0, 0);

    __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
    /* get the current revision from MSR 0x8B */
    rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);

    /* notify the caller of success on this cpu */
    uci->err = MC_SUCCESS;
    spin_unlock_irqrestore(&microcode_update_lock, flags);
    printk(KERN_INFO "microcode: CPU%d updated from revision "
           "0x%x to 0x%x, date = %08x \n", 
           cpu_num, uci->rev, val[1], uci->mc->hdr.date);
    return;
}


Windowsでは?
あまり情報がありませんが、サポート情報から推測するなら、WindowsXP/Windows Server 2003以降であれば、Linux同様にマイクロコードにパッチをあてる仕組みをWindows自身が持っていると考えていいでしょう。デバイスマネージャーでプロセッサを見ると、CPU用にドライバーファイルがロードされているのがわかりますが、おそらくこのドライバー内でパッチをあてているのではと推測されます。それ以前のもの(Windows95/98/Me/NT/2000など)については、たぶん実装されていないものと思われます。


FreeBSDでは?
FreeBSDにもLinux同様の仕組みがないかと気になり、FreeBSD4.8とFreeBSD5.3-beta5の両方のカーネルのソース(/usr/src/sys/i386)を調べてみましたが、見つけられませんでした。ないんでしょうか?FreeBSDファンなので、ちょっとがっかり。


AMD製CPUでは?

AMDのK8(Athlon64/Opteronなど)にもマイクロコードにパッチをあてる仕組みがあるらしいです。これを使うとBIOSに内蔵してシステムの起動時にパッチをあてることも、OS上からパッチをあてることもできます。その詳細が解明されてしまっています。手順としてはMSRを使って書き込むものですが、どうもパッチデータが暗号化されていないらしいです。


(2005/10/29追記)「Revision Guide for AMD Athlon64 and AMD Opteron Processors」(Revision 3.57, August 2005)によると、マイクロコードにパッチをあてる際に使うMSRはC001_0020hのようです。ただパッチをあてる詳細手順はわかりません。(AMD64のマニュアルに記載がない。)


起草日:2004年9月18日(土)(www.marbacka.net内の別のサイトで公開)
最終更新日:2017年2月19日(日)


ソ-ス:BIOSとマイクロコード



関連:
OSでマイクロコードのパッチを当てる | OPC Diary

ソフトウェア同様、CPUにもバグはある - @IT

マイクロプログラム方式