cpuidを使ってmodel nameを取得する

はじめに

` cat/proc/cpuinfo` で出力されるvendor_idやmodel nameを、自分でcpuidを呼んで調べてみる。拡張インラインアセンブラ構文の確認と、C言語の可変長引数関数の自分用覚書。

cpuid

cpuid命令の詳細はIntelウェブサイトの日本語技術資料のダウンロードにある、「IA-32 インテル® アーキテクチャー・ソフトウェア・デベロッパーズ・マニュアル、中巻 A: 命令セット・リファレンス A-M 」にある。

例えばeaxに0を代入した状態でcpuid命令を発行すると、ebx, ecx, edxにそれぞれ”Genu”、”ntel”、”ineI”の文字列が入る(32bitレジスタなので4つのcharが入る)。これをebx、edx、ecxの順番に並べれば「GenuineIntel」となる。

model nameについては、eaxに0x80000002、0x80000003、0x80000004を入れてcpuidを呼ぶと、eax,ebx,ecx,edxに4バイトずつ情報が入ってくる。従って、model nameを得るには、合計3回cpuidを呼ばないといけない。

インラインアセンブラ

cpuidを呼んだあとにeax,ebx,ecx,edxに入ってくる情報を変数にコピーする必要がある。これを拡張インラインアセンブラ構文でやる。例えばeaxに値を入れるところを、

uint32_t v = 0;
asm("nop" :: "a" (v));

とする。asmの:の後が拡張構文で、これはeaxにvの値を代入せよ、という意味。 また、

uint32_t a;
  asm("movl %%eax, %0" : "=r" (a));

とすれば、eaxの内容をaに代入できる。

まとめると、eaxにvの値を入れてcpuidを呼んで、eax〜edxの値を変数に読み出すところはこんな感じでできる。

  uint32_t eax, ebx, ecx, edx;
  asm("nop" :: "a" (v));
  asm("cpuid");
  asm("movl %%eax, %0" : "=r" (eax));
  asm("movl %%ebx, %0" : "=r" (ebx));
  asm("movl %%ecx, %0" : "=r" (ecx));
  asm("movl %%edx, %0" : "=r" (edx));

ソースコード

何度もcpuidを呼んで、eax〜edxの値を読み出し、文字列として出力するコード。

//------------------------------------------------------------------------
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
//------------------------------------------------------------------------
void
cpuid(uint32_t v, uint32_t &a, uint32_t &b, uint32_t &c, uint32_t &d){
  uint32_t eax, ebx, ecx, edx;
  asm("nop" :: "a" (v));
  asm("cpuid");
  asm("movl %%eax, %0" : "=r" (eax));
  asm("movl %%ebx, %0" : "=r" (ebx));
  asm("movl %%ecx, %0" : "=r" (ecx));
  asm("movl %%edx, %0" : "=r" (edx));
  a = eax;
  b = ebx;
  c = ecx;
  d = edx;
}
//------------------------------------------------------------------------
int
put(int num, ...){
  va_list args;
  va_start(args,num);
  char buf[256];
  buf[num*4] = 0;
  for(int i=0;i<num;i++){
    uint32_t v = va_arg(args,uint32_t);
    memcpy(buf+4*i,(char*)(&v),4);
  }
  printf("%s",buf);
  va_end(args);
}
//------------------------------------------------------------------------
int
main(void){
  uint32_t eax, ebx, ecx, edx;
  cpuid(0,eax,ebx,ecx,edx);
  put(3,ebx,edx,ecx);
  printf("\n");
  cpuid(0x80000002,eax,ebx,ecx,edx);
  put(4,eax,ebx,ecx,edx);
  cpuid(0x80000003,eax,ebx,ecx,edx);
  put(4,eax,ebx,ecx,edx);
  cpuid(0x80000004,eax,ebx,ecx,edx);
  put(4,eax,ebx,ecx,edx);
  printf("\n");
}
//------------------------------------------------------------------------

手元のSandyBridgeマシンでの実行例。

$ g++ modelname.cc
$ ./a.out
GenuineIntel
       Intel(R) Core(TM) i7-2700K CPU @ 3.50GHz
$ cat /proc/cpuinfo| grep Intel
vendor_id	: GenuineIntel
model name	: Intel(R) Core(TM) i7-2700K CPU @ 3.50GHz
vendor_id	: GenuineIntel
model name	: Intel(R) Core(TM) i7-2700K CPU @ 3.50GHz
vendor_id	: GenuineIntel
model name	: Intel(R) Core(TM) i7-2700K CPU @ 3.50GHz
vendor_id	: GenuineIntel
model name	: Intel(R) Core(TM) i7-2700K CPU @ 3.50GHz

できてるっぽい。