宇宙仮面の
C# Programming

 

WinRing0

開発環境: Visual Studio 2008

1.目的

暑くなってきましたね〜

現在使用しているメインPCが悲惨なことになっています。というのは、今のプロセッサは、Pentium 4 D なので、発熱がすごいです。このため、気温が高くなる5月〜10月ぐらいは、CPUの負荷が高くなると、キーボード、マウスがフリーズする事故頻発 フリーズ→強制再起動→OSクラッシュ→復旧再インストールせざるを得なくなることも2度ほどありました。一応 対策として筐体内に、ファンを1つ追加しましたが、効果はほとんどありませんでした。 温度モニターで監視 CPU メーターで監視しながら、だましだまし使っていますが、夏場はそれでもだめで、筐体外超強力大型ファン設置で、とりあえずしのいでいます。w

超大型ファン

そんなわけで、プロセッサの温度を取得する方法を調べていたら、WinRing0というものに出会いましたが、これが結構すごいもので、いろいろ調べたことを 2009年5月19日(火) 第40回codeseek勉強会 で話をさせていただきました。ここでは、そこで話した WinRing0 を使ったプロセッサの温度を取得する方法について説明します。

勉強会資料一式は、こちらからどおぞ。

2009年5月19日(火) 第40回codeseek勉強会資料 一式 約9MB

2.参考書

  1. OpenLibSys
    http://openlibsys.org/

  2. Intel® 64 and IA-32 Architectures Software Developer's Manuals
    http://www.intel.com/products/processor/manuals/

  3. Intel® 64 and IA-32 Architectures Software Developer's Manuals
    http://www.intel.com/products/processor/manuals/

  4. 宇宙仮面の C# プログラミング WMI

  5. 宇宙仮面のC# 研究室   CPU 温度の取得に関するメモ [2] [3] [4] [5] [6]

3.プロセッサ温度の謎

 

3.1 私のメインPC環境

私のメイン PC環境は、ASUS P5LD2-V マザーボードと、 Pentium 4 D です。 ASUS の PCProbe II というソフトウェアだと、次のようにプロセッサの温度、マザーボードの温度や、ファンのスピードなどを知ることができます。p>

ASUS PC Proble II

ところが、CPU Usage という Windows Gadget の情報では、次のようにプロセッサの負荷はわかりますが、温度はわかりません。

CPU Usage

一方、CPU Usage を eeePC  Atom N280 や、DELL Core2 で動かしてみると次のようにCPU温度を知ることができます。

 Atom N280

どうやらCPUの温度はプロセッサ、またはマザーボードに依存しているようです。どんな仕組みでCPU温度を取得しているのだろうと思って、いろいろ調べてみました。 

3.2 WMI でCPUの温度を調べてみる

WMI は、Windows Management Instrumentation の略で、Windows のシステム管理のためのインターフェースです。 温度に関しては、WMIに“Win32_TemperatureProbe” というインターフェースがありますのでこの方法でCPUの温度を取得できないか調べてみました。WMIは次のようなコードでアクセスすることができます。

ManagementObjectSearcher query1 = new ManagementObjectSearcher("SELECT * FROM Win32_TemperatureProbe");
var res = from ManagementObject item in query1.Get()
                     select new
                      {
                           Name = item["Name"],   
                           CurrentReading = item["CurrentReading"]  
                     };            
 if (res.Count() == 0)               
       e.Result = "Not supported“;

しかしながら、調査の結果、現在のPC “ASUS P5LD2-V”, eee PC 1000 HE では、このインターフェースはサポートされていないようで、CPU の温度をWMI経由で取得することはできませんでした。

3.3 MSR からCPU の温度を取得

別の方法として、MSR (Model Specific Register) からCPUの温度を取得することができます。

MSRとは、インテルプロセッサに搭載されているレジスターの一種で、次の特徴があります。このレジスターにアクセスすることにより、CPUの温度を取得することが可能です。

  • モデル固有のレジスターで、プロセッサのモデルによって仕様が異なる。

  • Intel Core 2 processor family, Intel Atom, Intel Core Duo, Intel Core Solo, Pentium 4 and Intel Xeon processors, P6 family processors, and Pentium processors に搭載

  • Timestamp, Platform ID, Microcode update, SMM Monitor, Frequency Clock Count,  Thermal Control, Thermal Statusなどの情報を取得・設定可能

  • rdmsr, wtmsr特権命令によりデータの取得・設定する。

Intelの仕様書を調べたら、現在のメインPCはPentium 4 D は MSRによるプロセッサー温度取得は非対応でした (T T)。

3.4 マザーボードの I/O コントローラ からCPU温度を取得

Intel の仕様書によると Pentium 4 Dでは、MSR から CPU の温度が取れないことが判明しました。しかし、ASUSのPC Proble II というソフトウェアでは、CPUの温度やファンのスピードなどのデータを取得することができています。どうしてだろうと調べていったらば、マザーボードの I/O コントローラー(W83627EHF) が、温度センサーが3つ搭載されていていて、そこから温度などの情報を取得していることがわかりました。

もう少し調べてみると、ASUS P5LD2-V では、CPUTIN Temperature、SYSTIN Temperature の2つのみのセンサーがサポートされているようです。このコントローラから情報を取得できれば、CPUやケースの温度を取得できるはずです。

しかし、I/Oコントローラへアクセスするための命令 IN/OUTは特権命令で、通常のアプリケーションからは直接操作できません。マザーボードの I/O コントローラー(W83627EHF)にアクセスして、レジスターの値を読み込むには、デバイスドライバー経由でしかアクセスできません。

4. Ring 0

Intel 64 and IA-32 Architectures  Software Developer’s Manual Volume1: Basic Architecture では、次の図のように、リング構成で特権レベルを構成しています。

Ring0

x86プロセッサは,レベル0(Ring0)からレベル3(Ring3)の4つの特権レベルを備えています。 リング0(最高特権レベルのカーネルモード)では、すべての特権命令の実行が可能 Windows NT アーキテクチャーでは、ユーザーモードとカーネルモードだけです。

I/O命令のような特権命令を実行するためには、Ring 0、すなわちカーネルモードに入る必要があります。 

4.1 特権命令とは

特権命令とは、 OSのみが使用できる特別な命令のことです。ユーザーが作成したプログラムが、システムが使う領域や資源(リソース)を破壊するのを防ぎます。

 特権モードへ切り換えは、 Intel IA-32 アーキテクチャでは、割込み(int 命令)により、割込ベクターを参照して割り込みハンドラにジャンプし、非特権モードから特権モードへ切り替えが起きます。このときのカーネル内の割り込みハンドらが呼び出され、カーネルだけが特権命令を実行できます。

4.2 非特権命令と特権命令の違い

たとえば、mov や cpuid は特権命令ではありません。ということは、ユーザー空間から直接実行が可能です。

char vender_sig[16];  
__asm
{
   mov eax, 0;                   /* Vender Signature を取得するindex */
   cpuid;                                              /* CPUID を実行*/
   mov dword ptr [vender_sig + 0], ebx;      /* 最初の4文字*/
   mov dword ptr [vender_sig + 4], edx;      /* 次の4文字*/
   mov dword ptr [vender_sig + 8], ecx;      /* 最後の4文字*/
   mov byte ptr [vender_sig + 12], 0;          /* \0 */  
}

printf(vender_sig);

上記コードでネイティブコードを作成したものは、一般ユーザーでも実行できます。

一方、hlt 命令は特権命令です。

  __asm {
   hlt;  
}

上記コードのように特権命令 hlt を実行しようとしても、次のように Privileged instruction 例外 (特権命令違反例外) が発生し、実行がトラップされることにより、実行できません。

Privileged instruction Exception

 

4.3 特権命令を実行するには

特権命令を実行するには、Windows の場合、カーネルモードに入るしかありません。カーネルモードに入るためには次のように各種デバイスドレイバーを書き、その中で実行する必要があります。

デバイスドライバー

http://itpro.nikkeibp.co.jp/article/Windows/20051111/224393/ より

5.WinRing0

以上より、CPUの温度を取得するためには、いずれにしてもデイバスドライバーを書かないと対応できないということになります。これでは簡単にCPUの温度は取得できないなぁ・・・と諦めかけていたところ、WinRing0に出会いしました。

WinRing0 は OpenLibSys から提供されており、この機能を使うことにより、非常に簡単にデバイスにアクセスすることが可能になります。作者は、ひよひよさんです。

WinRing0とは、Windows 用のハードウェアアクセスライブラリで、主要機能は I/O ポート、MSR (Model-Specific Register)、PCI バス へのアクセス機能 などを提供しています。サポートする OSは、X86では2008/Vista/2003/XP/2000/NT4/Me/98、および x64では 2008/Vista/2003/XP をサポートしています。

 詳細は、こちらを参照してください。

http://openlibsys.org/index-ja.html

このWinRing0を利用することにより、デバイスドライバーを開発しなくても、MSRやIOチャネルへのアクセスを簡単に行うことができてしまうというすぐれものです。

5.1 C# からの使用方法

WinRing0 を C# から使用するには、次の手順で行います。

  1. WinRing0.dll, WinRing0x64.dll, WinRing0.sys, WinRing0x64.sys および WinRing0.vxd をアプリケーションの実行ファイルと同じディレクトリにコピーします。

  2. OpenLibSys.cs をプロジェクトに加えます。

  3. using OpenLibSys; 文をソースコードに加えます。

  4. Ols ols = new Ols(); で Ols クラスのオブジェクトを生成します。

  5. ols.GetStatus() および ols.GetDllStatus() を呼び出し、正常に初期化が完了したかどうかを確認します。

  6. ols オブジェクトを介してライブラリの機能を呼び出します。  

  7. ols オブジェクトの後処理を実行。

  8. なお、WinRing0 を用いたアプリケーションでは必ず管理者権限が必要です。

5.2 WinRing0 を使用して、MSRからCPUの温度を取得する

WinRing0 で、MSRからCPUの温度を取得することができます。

アセンブラでは、RDMSR 命令で、インデックス 0x19c で呼び出すと、EDX:EAX レジスタに 64bit の値が帰ります。 その中の 22:16 bit が Thermal Sensor のデジタルリードアウトになります。この値が、CPUのTjMax からの差分となります。Atom N280 の TjMax は 105℃ です。

 ols.Rdmsr(0x19c, ref eax, ref edx);
             uint temp = (eax >> 16) & 0x7f;
             uint TjMax = 105;
             Console.WriteLine( (TjMax-temp).ToString() + "\n“);

 ※ CPUIDでこの機能がサポートされているかチェックする必要があります。

5.3 コード

OpenLibSys.Ols ols = new OpenLibSys.Ols();
ols.InitializeOls();


uint
res;

if ((res = ols.GetDllStatus()) != 0)
{
   
MessageBox.Show("GetDllStatus = " + res);
   
Application.Exit();
}

uint index = 0;
uint eax = 0x0;
uint ebx = 0x0;
uint ecx = 0x0;
uint edx = 0x0;

ols.Cpuid(0x06,
ref eax, ref ebx, ref ecx, ref edx);
if ((eax & 1) == 0)
{
    textBox1.Text +=
"CPU Temp: not supported \r\n";
}

// RDMSR ----IA32_THERM_STATUS ... 0x19c
//
// EDX: EAX
// 22:16 Digital Readout


ols.Rdmsr(0x19c,
ref eax, ref edx);

uint temp = (eax >> 16) & 0x7f;
uint TjMax = 105;
textBox1.Text +=
"CPU Temp: " + (TjMax-temp).ToString() + "\r\n";

ols.DeinitializeOls();

5.4 コントローラ W83627EHF からの温度取得

Pentium 4 では、MSRがサポートされていないので、この方法ではCPU温度を取得できません。一方、ASUS P5LD2-Vでは、IOコントローラとして W83627EHF を搭載しており、 このチップには温度センサーが3つ搭載されています。ただし、マザーボードASUS P5LD2-V上で使用されているのは、CPUTIN Temperature、SYSTIN Temperature の2つのみで、AUXTIN Temperatureは使用されていません。

したがって、このコントローラに WinRing0 経由でアクセスしてあげれば、CPUの温度や、マザーボードの温度を取得することができます。

このコントローラーからデータを取得するには、0x295 ポートにレジスタを指定、0x296 ポートからデータを取得するという手順になります。

ただし、バンクが 0, 1, 2, 3, 4 とあり、0x50 以上のレジスターにアクセスするには、バンクを切り替える必要があります。バンクの切り替え方法は、0x4E ポートの下位3ビットで指定します。

CPUTIN(CPUの温度)は、 次のとおりです。

  • バンク1、0x50 レジスタにハイビット(温度 ℃)

  • バンク1、0x51 レジスタにロービット(温度 7ビット目が立っていれば +0.5℃)

//=======================================
// CPUTIN Temperature
//=======================================
// バンク1
ols.WriteIoPortByte(0x295, 0x4E);
ols.WriteIoPortByte(0x296, 0x81);

// データ読出し(50h Bank1)
ols.WriteIoPortByte(0x295, 0x50);
hi = ols.ReadIoPortByte(0x296);  

// バンク1 ols.WriteIoPortByte(0x295, 0x4E);
ols.WriteIoPortByte(0x296, 0x81);

// データ読出し(10h Bank1)
ols.WriteIoPortByte(0x295, 0x51);
lo = ols.ReadIoPortByte(0x296);  

float CPUTIN = hi + ((lo & 0x80) != 0 ? 0.5f : 0);

6. WinRing0 on Web Service

WinRing0 を Web Service で動かした時のメモです。通常の WinRing0 のアプリケーションと違って、実行時のDLLやドライバーのインストール場所に注意する必要があるので、設定などをメモっておきます。

6.1 ビルド環境の構築 (Vista 32bit版)

  1. Visual Studio 2008 を管理者モードで起動

  2. ASP.NET Web サービスアプリケーションのプロジェクトを作成

  3. OpenLibSys.cs を追加

  4. WinRing0 は管理者で実行する必要があるため、Web.Config で Administrator に偽装するためのコードを追加

  5. デバッグ環境時の DLL 参照パス C:\Program Files\Common Files\Microsoft Shared\DevServer\9.0 に WinRing0.sys と WinRing0.dll をコピーする。

  6. IIS実行環境時の DLLL参照パス c:\windows\system32\inetsrv に WinRing0.sys と WinRing0.dll をコピーする。参照パス c:\windows\system32\inetsrv に WinRing0.sys と WinRing0.dll をコピーする。

以上の応用より、Web Service で次のように温度を取得することも可能になります。

Web Service  

ただし、作り方によっては、一般ユーザーにRing0を触らせる手段を提供してしまうことになりますので、セキュリティには十分な注意が必要です。

7.今後の予定

本当は、Windows Gadget から Web Service を呼び出して、もう少し面白そうなことをしたかったのですが・・・今のところ時間不足でここまでです。