Top / CodeZine / CPUID 命令の考察

CPUID命令によるCPUの性能・機能の把握 (CodeZine) として公開されています。

完成図

littleCPUID の完成図

はじめに

この記事ではインテル系の CPU に実装されている CPUID 命令がどのように働いているかを考察します。

今回、デモプログラム(littleCPUID) を作成しました。このプログラムは実際に使用しているシステムから CPUID 命令を実行し、その結果を出力します。

対象読者

  • CPUの働き、特に CPUID 命令がどのように働くのか興味のある方
  • Visual C++ によるインラインアセンブラの実装に興味がある方

必要な環境

Visual C++ version 6 SP6(MFC)で開発を行っています。

実行環境としては Windows 2000/XP を想定しています。現行では Windows 9x 系では実行出来ないはずです。これは 2000/XP のみで使用される API を使用しているためで、それらを明示的に Loadlibrary すれば対応出来るのですが、面倒なのでやっていません。

CPUID (CPU Identification)

システムのプロパティ表示の不思議

まずは下記の「システムのプロパティ」を見てもらいたい。

システムのプロパティ表示の CPU 表示部分に注目

CPU 名が表示されているのですが、CPU 名の頭に余計な空白が入っています。 この現象はインテル社の Pentium 4 以降の CPU でのみで発生しており、AMD 社の CPU では 発生していません。

実はこの空白の原因は CPUID 命令の動作に起因した現象と考えています。

CPUID(CPU Identification) 命令とは

CPUID 命令(プロセッサ識別) は、CPU の基本的な命令コードである MOV 命令(データ移動) や ADD 命令(加算)や、IN/OUT 命令(外部入出力)と同じくインテル系の CPU には必ず実装されている命令コードです。

CPUID 命令は主に以下の目的で使用されます。

  1. 新たに実装された新機能命令の有無
    CPUID は、OS やソフトウェアが予め新機能が実装されているかを確認するために使用されます。
    インテル系の CPU は、発展に伴い、多くの命令コードや機能が追加されました。例えばマルチメディア機能に特化した MMX/SSE 命令、データ保護機能である NX-bit/XD-bit機能、64-bit 対応である AMD64/EM64T 等がそうです。OS やソフトウェアがこれらの新機能を使用する場合、CPU が新機能をサポートしているかどうか確認する必要があります。 なぜなら新機能をサポートしていない CPU が新機能を使用しようとした場合、大概は一般保護エラー(未定義の実行コード)として扱い、その後の処理を中断してしまうからです。DOS のような保護されていない OS 上で一般保護エラーを発生させた場合、最悪、元の制御に戻れない(ハングアップ?)ことも予想されます。予め新機能をサポートしてるかどうかを確認しておくことで、新機能を使用するか、従来の機能で乗り切るか、そもそも実行しないかの切り分けが可能になります。
  2. CPU ベンダーの判別
    CPUID は各 CPU ベンダーのブランドを判別するために使用されます。
    インテル系の CPU は、インテル社以外にも AMD 社や VIA 社等多くのベンダーが各々開発を行っています。各社は CPUID に署名やブランド名を実装することで、自社のブランドを誇示しています。近年、AMD 社やインテル社は機能以外にも自社の CPU に独自のブランド番号(AMD 社はモデルナンバー、インテル社はプロセッサ・ナンバ)を使用しています。CPUID は正確なブランド番号を取得出来る仕組みが実装されています。
  3. プロセッサ・シリアル・ナンバー (PSN, Processor Serial Number)
    CPUID はかつてプロセッサ・シリアル・ナンバー(PSNと略) という CPU 固有シリアル番号を返却するために使用されていました。
    CPU 毎に割り振られ重なることのない番号を割り振ることで PC 使用者を特定することが出来ます。PSN を利用したサービスとしてDRM 等が考えられていましたが、同時にプライバシー問題に関わるとして後に PSN は削除されました*1

CPUID 命令の動作

インテルのサイトで配布されている 日本語技術資料の 「IA-32 インテル アーキテクチャ・ソフトウェア・デベロッパーズ・マニュアル 中巻」に以下の記述があります。

オペコード命令説明
0F A2CPUIDEAX レジスタに最初に入力された値に応じて、プロセッサの識別情報と機能情報をEAX、EBX、ECX、EDX の各レジスタに返す。

レジスタ EAX に所定のインデックス値を入れ CPUID 命令を実行することで EAX、EBX、ECX、EDX に結果を出力します。

インデックス値に対して以下の値が返却されます。 以下の説明で CPUID(xxx) と表現されていますが、これは EAX=xxx を設定し CPUID 命令を実行した結果を指します。

CPUID 情報

CPUID(2) 以降は、Intel がほぼ独占状態のようです。

CPUID入力出力詳細
CPUID(0)EAXCPUID の有効最大値
EBX:EDX:ECXVendor ID
CPUID(1)EAXプロセッサ・シグネチャ(Family/Model/Stepping...)
EBX,ECX,EDXFunction Flags
CPUID(2)EAX,EBX,ECX,EDXCPU キャッシュ情報
CPUID(3)EAX,EBX,ECX,EDXPSN

一般的に Vendor ID で製造 CPU 会社を判断し、プロセッサ・シグネチャで Pentium III/Pentium 4/Athlon 等の CPU の種類を判断します。 CPU の種類で CPU の機能を判断してはならず(例えば「Pentium III なら全て MMX をサポートしているはずだ」という決め打ち)、「Function Flags」にて CPU の機能を検出し判断します。例えば CPUID(1) の EDX の値の 23 bit 目が立っている場合、MMX を サポートしていると判断します。

拡張 CPUID 情報

拡張 CPUID は AMD 社が活用している領域です。Pentium 4 より Intel も活用し始めたようです。

CPUID入力出力詳細
CPUID(8000-0000h)EAX拡張 CPUID の有効最大値
EBX:EDX:ECXReserved(Intel)
CPUID(8000-0001h)EAX,EBX,ECX,EDX拡張 Function Flags
CPUID(8000-0002h)EAX,EBX,ECX,EDXプロセッサ・ブランド・ストリング
CPUID(8000-0003h)EAX,EBX,ECX,EDXプロセッサ・ブランド・ストリング(続き)
CPUID(8000-0004h)EAX,EBX,ECX,EDXプロセッサ・ブランド・ストリング(続き)

CPUID 命令の実際

実際に CPUID 命令を実行してみましょう。実際に実行するにあたっては Windows 標準搭載されている debug.exe 互換の EXDEB というソフトウェアを使用します。コマンド プロンプトより EXDEB を実行し、以下の手順にて動作確認をします。

  1. r1 コマンドの実行
    プログラムを実行する前のレジスタ情報を見ます。
  2. a コマンドの実行
    ハンドアセンブルによるプログラミングを行います。以下の命令を打ち込んでください。<Enter> はエンターキーを示します。
    • mov eax, 0 <Enter>」 (EAX レジスタに 0 を挿入します)
    • cpuid <Enter>」 (CPUID 命令を実行します)
    • int 3 <Enter>」 (実行を中断します)
    • 「<Enter>」
  3. u コマンドの実行
    今までの打ち込んだ結果を逆アセンブルします。正しく打ち込まれているか確認できます。
  4. g コマンドの実行
    プログラムを実行します。100番地から int 3 までプログラムを実行します。以下の実行結果より EAX, EBX, ECX, EDX が変化していることが分かります。
  5. q コマンドの実行
    EXDEB から抜けます。

以下が上記の実行例になります。

D:\>exdeb <Enter>
Extended Debugger  Ver1.85  94,97/07  by Sey_ju_row
CPU [Pentium II]  FPU [Present]  Machine [PC-98 DOS/V]
-r1 <Enter>
EAX=000F:0000    EBX=0000:0000    ECX=0000:0000    EDX=6974:0000
ESP=0000:0000    EBP=0000:0000    ESI=0000:0000    EDI=0000:0000
DS=5F51  ES=0000  SS=5F51  FS=0000  GS=0000  CS=5F51  IP=0100  -N11--I---------
5F51:0100 66B800000000   MOV    EAX,00000000
-a <Enter>

5F51:0100       mov eax, 0
5F51:0106       cpuid
5F51:0108       int 3
5F51:0109
-u <Enter>
5F51:0100 66B800000000   MOV    EAX,00000000
5F51:0106 0FA2           CPUID
5F51:0108 CC             INT    3
5F51:0109 0000           ADD    [BX+SI],AL
5F51:010B 0000           ADD    [BX+SI],AL
5F51:010D 0000           ADD    [BX+SI],AL
5F51:010F 0000           ADD    [BX+SI],AL
5F51:0111 0000           ADD    [BX+SI],AL
-g <Enter>
EAX=0000:0001    EBX=6874:7541    ECX=444D:4163    EDX=6974:6E65
ESP=0000:0000    EBP=0000:0000    ESI=0000:0000    EDI=0000:0000
DS=5F51  ES=0000  SS=5F51  FS=0000  GS=0000  CS=5F51  IP=0108  -N11--I---------
5F51:0108 CC             INT    3
-q <Enter>

D:\>

上記のケースは CPUID のインデックス値が 0 の時に以下の値が返却されました。

EAX = 0x1
EBX = 0x68747541 ("htuA")
ECX = 0x444D4163 ("DMAc")
EDX = 0x69746E65 ("itne")

EBX:EDX:ECX (リトルエンディアン)の順に並べると ASCII で「AuthenticAMD」になります。この文字列によりこの CPU は AMD 社が開発したものと判断できます。

EAX が 1 であるということは CPUID のインデックス値は 0 と 1 のみ有効であるということが分かります。これは昔から AMD 社の CPU は 0x80000000 以上の Extended CPUID の値を中心に情報を返却する方針を取っているからのようです。余談ですが、後にインテル社も Pentium 4 辺りから Extended CPUID をサポートすることになります。

以下は、同様に Intel 社製の CPU(Pentium 4) で上記のプログラムを実行した結果です。

EAX = 0x2
EBX = 0x756E6547 ("uneG")
ECX = 0x6C65746E ("letn")
EDX = 0x49656E69 ("Ieni")

EBX:EDX:ECX (リトルエンディアン)の順に並べると ASCII で「GenuineIntel」になります。この文字列によりこの CPU はインテル社が開発したものと判断できます。また EAX が 2 であるということは CPUID のインデックス値は 0、1、2 が有効であるということが分かります。

littleCPUID の実装

littleCPUID によるデモ

littleCPUID には3つの機能を実装しています。

  • CPUID 命令表示機能 『Dump』
    CPUID 命令で取得できたデータの全ての値を出力します。プロセッサが複数ある場合、各プロセッサ毎に取得します。
  • CPU がサポートしている機能の表示 「Feature Flags」
    CPUID 命令より CPU がどの機能をサポートしているかを判断し表示します。サポートしていない機能についてはグレーで表示し、ボタンが押せないようにしています。完成図の表示例の場合、「MMX」や「SSE」はサポートしているものの「HTT(Hyper-Threading)」はサポートしていないことが分かります。
    また実際には使用できないケースでも CPU が対応している場合は有効と表示しています。例えば Windows XP(32-bit版)で動作していても CPU が x64(AMD64/EM64T) に対応している場合は、表示は有効になります。また、EIST や Hyper-Threading 機能等の場合、CPU 以外にも BIOS や Chipset の対応も必要で、CPU のみが対応していてもトータルで対応出来ない場合があります。この場合でも CPU が対応していると Flag を立てているのを優先し、有効としています。CPU が対応しているからと言って対応可能だと判断しないよう気をつけてください。
  • SyncHack への接続 「Enable SyncHack Connection」
    この設定が有効(デフォルトでは無効)にし「Feature Flags」のボタンを押すことで該当する SyncHack の page に接続する機能を加えました。SyncHack は私が運営する page で、より詳しい内容を掲載出来たら面白いなと思い、実装しました。
    今の所、まだあまり SyncHack の方が充実していないので面白くないです。ごめんなさい。

littleCPUID のアルゴリズム

CPUID の特性上、動的に変更されることはほとんど無いはずです。よって起動時に CPUID 命令で取得できるデータを取れるだけ取って、そのデータを使いまわすことにしました。以下のアルゴリズムで全ての CPUID の値を取得しています。

  1. CPUID(0) の取得
    CPUID 命令をサポートしている CPU は必ず何らかの値を返却します。CPUID(0)が返却されない場合、CPUID 命令をサポートしていないと判断します。また CPUID(0) の返却値 EAX より CPUID の最大長が分かります。例えば EAX が 4 の場合、CPUID(0)、CPUID(1)、CPUID(2)、CPUID(3)、CPUID(4) で有効な値が返却されます。このケースでは例えば最大長以上の CPUID(1000) を取得しようとした場合、CPUID の最大値である CPUID(4) の値が返却されます。
  2. CPUID(x) の取得
    CPUID(0) の EAX より有効な CPUID(0)-CPUID(x) の全ての値を取得します。
  3. CPUID(8000-0000h) の取得
    8000-0000h より上の CPUID は 拡張 CPUID として区別されており、別途対応可能か確認する必要があります。Extended CPUID の場合も EAX に最大長が返却されます。例えば CPUID(8000-0000h) の EAX が 8000-0004h だった場合、CPUID(8000-0000h)、CPUID(8000-0001h)、CPUID(8000-0002h)、CPUID(8000-0003h)、CPUID(8000-0004h) が有効な値となります。
  4. 拡張 CPUID(y)の取得
    CPUID(8000-0000h) の EAX より有効な CPUID(8000-0000h)-CPUID(y) の全ての値を取得します。

「システムのプロパティ表示の不思議」の考察

以下は上記の「システムのプロパティ」を表示している PC の CPUID です。

idx=[00000000], eax=[00000002], ebx=[756E6547], ecx=[6C65746E], edx=[49656E69]
idx=[00000001], eax=[00000F27], ebx=[00010809], ecx=[00000400], edx=[BFEBFBFF]
idx=[00000002], eax=[665B5101], ebx=[00000000], ecx=[00000000], edx=[007B7040]
idx=[80000000], eax=[80000004], ebx=[00000000], ecx=[00000000], edx=[00000000]
idx=[80000001], eax=[00000000], ebx=[00000000], ecx=[00000000], edx=[00000000]
idx=[80000002], eax=[20202020], ebx=[20202020], ecx=[20202020], edx=[6E492020]
idx=[80000003], eax=[286C6574], ebx=[50202952], ecx=[69746E65], edx=[52286D75]
idx=[80000004], eax=[20342029], ebx=[20555043], ecx=[30342E32], edx=[007A4847]

ちなみに 0x20 は ASCII 文字で表すと空白(' ')を示します。どうやら「システムのプロパティ」は拡張 CPUID の「プロセッサ・ブランド・ストリング(80000002-80000004h)」が影響しているようです。上記のレジスタの値より「プロセッサ・ブランド・ストリング」を ASCII で表すと以下の文字列になります('['、']' は含みません)。

[              Intel(R) Pentium(R) 4 CPU 2.40GHz]

つまり先頭の空白はシステムのプロパティが「プロセッサ・ブランド・ストリング」をそのまま出力していると考えられます。

ちなみに CPUID は Intel 社が開発したものですが拡張 CPUID は、AMD 社が開発しています。AMD 社の「プロセッサ・ブランド・ストリング」は頭に空白は無く、先頭から文字列が始まっています。一方、Intel 社は Pentium 4 より拡張 CPUID を採用していますが、採用当初から先頭が空白になっていたようです。

なぜ先頭を空白にしているかは私には分かりませんでした。多分、技術的以外の問題がそうさせているのではないでしょうか。

  • MSKB:216204 Intel Pentium または Celeron の CPU が正しく識別されない
  • MSKB:416349 [WinMe] Pentium 4 の名称がずれて表示される
  • MSKB:316965 Windows XP でプロセッサ速度が誤って報告される

まとめ

CPUID 命令により、CPU に実装されている多くの機能をソフトウェアより知ることが可能であることが分かりました。

CPUID 命令を使用することにより「予め」ソフトウェア側で CPU の性能・機能を知ることが出来、新しい機能を使用するか、古い機能で代用するかを切り分ける用途にも使用できます。

このような場面でも CPUID 命令は使用されている、という一例

最近、米 AMD 社が米 Intel 社に対して独占禁止法の違反があるとして訴訟を起こしたニュースが話題になっています。この訴訟内容の一つにインテル製のコンパイラが CPUID にて AMD 社製の CPU かどうか検知し、意図的に最適化されないパスに落とし込んでいる、と訴えています。AMD 社の言っていることも尤もなのですが、Intel 社からしても販促の意味合いの強いコンパイラで他社のチューニングまで保障するか、と言われるとそれは難しいことなのかもしれません。

最後にお願いなど

現在、「CPUID 命令の考察」の続きモノとして「マルチコアの検出方法の調査」というのを書いている最中です。CPUID の情報だけでマルチコア、Hyper-Threading、Multi-processing を検出し、物理的に使用している CPU 数を検出するサンプルプログラムの作成をしたら面白いんじゃないか、と考えています。

一応、仕様書に記載している通りに実装する所までは終えています。 ただ今の所、私の手元にはデュアルコアや Multi-processing の実機が無く、実際動作するかどうか確認出来ない状態です。申し訳ないのですが下記の CPU をお持ちの方、是非『Dump』情報の提供に協力してください。CPU-Z のテキストダンプでも可能ですが CrystalCPUID の場合、情報が足りません。

  • Pentium Extreme Edition で Processor が 4 つ見えている状態
  • Opteron(Dual-core)*2 で Processor が 4 つ見えている状態
  • Pentium D で Processor が 2 つ見えている状態
  • Athlon 64 X2 で Processor が 2 つ見えている状態
  • Opteron *2(Multi-processor) で Processor が 2 つ見えている状態

送信方法は CodeZine の送信箱 でも構いませんし、直接、一番下にあるボタンより「メッセージを投稿」より投稿してもらっても構いません。

確認出来次第「マルチコアの検出方法の調査」の方、投稿します。

littleCPUID 自身は Intel/AMD 以外はサポートしていません(Other CPU として扱っています)。他の CPU ももっとサポートしてほしいとお考えのアナタ、サポートはやぶさかではありません。実機を持っていないことにより対応出来ていないだけで、アナタの協力さえあれば対応は可能です。

是非、よろしくお願いします。

参考資料

インテル

  • 日本語技術資料のダウンロード
    上記のサイトにある「AP-485 インテル プロセッサの識別と CPUID 命令」という資料が役に立ちます。翻訳された AP-485 は最新版の英語版とは同期が取れていませんので、常に最新情報という訳ではありませんので注意してください。必要があれば英語版を別途取得しましょう。

AMD

  • AMD Athlon 64 Processor Tech Docs
    上記のサイトにある「BIOS and Kernel Developer's Guide for AMD Athlon 64 and AMD Opteron Processors」(26094) という資料が役に立ちます。URL は変わりやすいので見つからない場合には再度、上記の資料を検索し直してみてください。

参考にしたサイト

参考にしたツール類

  • EXDEB
    EXDEB の配布場所。旧 NIFTY-Serve のフォーラムを中心として開発されていました。Windows 標準で付属されている debug.exe(debug.com) ではレジスタが 16-bit までしか扱うことが出来なく、しかも CPUID や MMX 等の比較的新しいニーモニックも未だにサポートされていません。
    EXDEB は 32-bit までレジスタを扱うことが出来、しかも CPUID 命令や MMX 等のニーモニックにも対応しています。私は Quick Hack によく使用していました。
  • http://www.cpuid.com/
    CPU-Z の配布場所。CPUID 情報より CPU の情報を表示するソフトウェアです。CPUID 以外にも多くの情報を表示出来ます。
  • http://crystalmark.info/
    CrystalCPUID の配布場所。CPU-Z と同じく CPUID 情報より CPU の情報を表示するソフトウェアです。こちらも CPUID 以外の PC 情報を表示できます。日本の方が開発していますのでコミュニケーションも日本語で出来るのはメリットかも。しかも Source も公開しています。素晴らしい。

関連書籍

  • はじめて読む8086―16ビット・コンピュータをやさしく語る アスキーブックス
    私がアセンブラに携わった時に初めて読んだ書籍です。特に MS-DOS より付属されている DEBUG コマンドによる実習は非常に有用な情報でした。今回、EXDEB による CPUID 命令のテストを思いついたのもこの書籍のお陰です。
    8086 ベースなので今見るとかなり古い情報になってしまいますが、CPU の基本命令である MOV 命令や ADD(ADC)/SUB(SBB) 命令等、その場でアセンブラを体験できる楽しみはこの書籍以外には見たことがありません。
    これから初めてアセンブラを学んでみたいと考えている方に 8086 系 CPU を勧めるのは若干気がひける*2のですが、その場で体験できる実感はなにより大切ではないかと考えています。
  • はじめて読むPentium マシン語入門編 (ASCII)
    実習環境が DEBUG コマンド → Visual C++ に変更されています。現状に即したアセンブラ開発が学べると思います。ちなみに littleCPUID も Visual C++ のインラインアセンブラ機能を使用して開発を行っています。

*1 PSN はインテルの Pentium III で実装され Pentium 4 で削除されています
*2 セグメントとかオフセットとか他の CPU のアーキテクチャを比較すると異様ですし

添付ファイル: filelittlecpuid.gif 1046件 [詳細] fileproperty.gif 881件 [詳細]

Last-modified: 2005.09.25 (日) 07:23:10 (4382d)