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.参考書
-
OpenLibSys
http://openlibsys.org/
-
Intel® 64 and IA-32
Architectures Software Developer's Manuals
http://www.intel.com/products/processor/manuals/
-
Intel® 64 and IA-32 Architectures Software Developer's Manuals
http://www.intel.com/products/processor/manuals/
-
宇宙仮面の C# プログラミング WMI
-
宇宙仮面のC# 研究室 CPU 温度の取得に関するメモ [2]
[3]
[4]
[5]
[6]
3.プロセッサ温度の謎
3.1 私のメインPC環境
私のメイン PC環境は、ASUS P5LD2-V マザーボードと、 Pentium 4 D です。
ASUS の PCProbe II
というソフトウェアだと、次のようにプロセッサの温度、マザーボードの温度や、ファンのスピードなどを知ることができます。p>
ところが、CPU Usage という Windows Gadget
の情報では、次のようにプロセッサの負荷はわかりますが、温度はわかりません。
一方、CPU Usage を eeePC Atom N280 や、DELL Core2
で動かしてみると次のようにCPU温度を知ることができます。
どうやら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
では、次の図のように、リング構成で特権レベルを構成しています。
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 例外 (特権命令違反例外) が発生し、実行がトラップされることにより、実行できません。
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# から使用するには、次の手順で行います。
-
WinRing0.dll, WinRing0x64.dll, WinRing0.sys,
WinRing0x64.sys および WinRing0.vxd をアプリケーションの実行ファイルと同じディレクトリにコピーします。
-
OpenLibSys.cs をプロジェクトに加えます。
-
using OpenLibSys; 文をソースコードに加えます。
-
Ols ols = new Ols(); で Ols クラスのオブジェクトを生成します。
-
ols.GetStatus() および ols.GetDllStatus()
を呼び出し、正常に初期化が完了したかどうかを確認します。
-
ols オブジェクトを介してライブラリの機能を呼び出します。
-
ols オブジェクトの後処理を実行。
なお、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の温度)は、 次のとおりです。
//=======================================
//
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版)
-
Visual Studio 2008 を管理者モードで起動
-
ASP.NET Web サービスアプリケーションのプロジェクトを作成
-
OpenLibSys.cs を追加
-
WinRing0 は管理者で実行する必要があるため、Web.Config で
Administrator に偽装するためのコードを追加
-
デバッグ環境時の DLL 参照パス C:\Program Files\Common
Files\Microsoft Shared\DevServer\9.0 に WinRing0.sys と WinRing0.dll
をコピーする。
-
IIS実行環境時の DLLL参照パス c:\windows\system32\inetsrv に WinRing0.sys と
WinRing0.dll をコピーする。参照パス c:\windows\system32\inetsrv に WinRing0.sys と
WinRing0.dll をコピーする。
以上の応用より、Web Service
で次のように温度を取得することも可能になります。
ただし、作り方によっては、一般ユーザーにRing0を触らせる手段を提供してしまうことになりますので、セキュリティには十分な注意が必要です。
7.今後の予定
本当は、Windows Gadget から Web Service
を呼び出して、もう少し面白そうなことをしたかったのですが・・・今のところ時間不足でここまでです。