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 
	  を呼び出して、もう少し面白そうなことをしたかったのですが・・・今のところ時間不足でここまでです。