Hardware/CPU/IntelCPU.cs
author moel.mich
Tue, 24 Aug 2010 20:14:54 +0000
changeset 174 51d62ed28ff5
parent 166 fa9dfbfc4145
child 182 4801e9eaf979
permissions -rw-r--r--
Fixed Issue 105 and Issue 106.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     5   The contents of this file are subject to the Mozilla Public License Version
     6   1.1 (the "License"); you may not use this file except in compliance with
     7   the License. You may obtain a copy of the License at
     8  
     9   http://www.mozilla.org/MPL/
    10 
    11   Software distributed under the License is distributed on an "AS IS" basis,
    12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    13   for the specific language governing rights and limitations under the License.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    17   The Initial Developer of the Original Code is 
    18   Michael Möller <m.moeller@gmx.ch>.
    19   Portions created by the Initial Developer are Copyright (C) 2009-2010
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s):
    23 
    24   Alternatively, the contents of this file may be used under the terms of
    25   either the GNU General Public License Version 2 or later (the "GPL"), or
    26   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    27   in which case the provisions of the GPL or the LGPL are applicable instead
    28   of those above. If you wish to allow use of your version of this file only
    29   under the terms of either the GPL or the LGPL, and not to allow others to
    30   use your version of this file under the terms of the MPL, indicate your
    31   decision by deleting the provisions above and replace them with the notice
    32   and other provisions required by the GPL or the LGPL. If you do not delete
    33   the provisions above, a recipient may use your version of this file under
    34   the terms of any one of the MPL, the GPL or the LGPL.
    35  
    36 */
    37 
    38 using System;
    39 using System.Collections.Generic;
    40 using System.Diagnostics;
    41 using System.Globalization;
    42 using System.Reflection;
    43 using System.Runtime.InteropServices;
    44 using System.Threading;
    45 using System.Text;
    46 
    47 namespace OpenHardwareMonitor.Hardware.CPU {
    48   internal sealed class IntelCPU : Hardware, IHardware {
    49 
    50     private int processorIndex;
    51     private CPUID[][] cpuid;
    52     private int coreCount;
    53     
    54     private string name;
    55 
    56     private uint family;
    57     private uint model;
    58     private uint stepping;
    59 
    60     private Sensor[] coreTemperatures;
    61 
    62     private Sensor totalLoad;
    63     private Sensor[] coreLoads;
    64     private Sensor[] coreClocks;
    65     private Sensor busClock;    
    66     private bool hasTSC;
    67     private bool invariantTSC;    
    68     private double estimatedMaxClock;
    69 
    70     private CPULoad cpuLoad;
    71 
    72     private ulong lastTimeStampCount;    
    73     private long lastTime;
    74     private uint maxNehalemMultiplier = 0;    
    75     
    76     private const uint IA32_THERM_STATUS_MSR = 0x019C;
    77     private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
    78     private const uint IA32_PERF_STATUS = 0x0198;
    79     private const uint MSR_PLATFORM_INFO = 0xCE;
    80 
    81     private string CoreString(int i) {
    82       if (coreCount == 1)
    83         return "CPU Core";
    84       else
    85         return "CPU Core #" + (i + 1);
    86     }
    87 
    88     private float[] Floats(float f) {
    89       float[] result = new float[coreCount];
    90       for (int i = 0; i < coreCount; i++)
    91         result[i] = f;
    92       return result;
    93     }
    94 
    95     public IntelCPU(int processorIndex, CPUID[][] cpuid, ISettings settings) {
    96 
    97       this.processorIndex = processorIndex;
    98       this.cpuid = cpuid;
    99       this.coreCount = cpuid.Length;
   100       this.name = cpuid[0][0].Name;
   101 
   102       this.family = cpuid[0][0].Family;
   103       this.model = cpuid[0][0].Model;
   104       this.stepping = cpuid[0][0].Stepping;
   105 
   106       float[] tjMax;
   107       switch (family) {
   108         case 0x06: {
   109             switch (model) {
   110               case 0x0F: // Intel Core (65nm)
   111                 switch (stepping) {
   112                   case 0x06: // B2
   113                     switch (coreCount) {
   114                       case 2:
   115                         tjMax = Floats(80 + 10); break;
   116                       case 4:
   117                         tjMax = Floats(90 + 10); break;
   118                       default:
   119                         tjMax = Floats(85 + 10); break;
   120                     }
   121                     tjMax = Floats(80 + 10); break;
   122                   case 0x0B: // G0
   123                     tjMax = Floats(90 + 10); break;
   124                   case 0x0D: // M0
   125                     tjMax = Floats(85 + 10); break;
   126                   default:
   127                     tjMax = Floats(85 + 10); break;
   128                 } break;
   129               case 0x17: // Intel Core (45nm)
   130                 tjMax = Floats(100); break;
   131               case 0x1C: // Intel Atom (45nm)
   132                 switch (stepping) {
   133                   case 0x02: // C0
   134                     tjMax = Floats(90); break;
   135                   case 0x0A: // A0, B0
   136                     tjMax = Floats(100); break;
   137                   default:
   138                     tjMax = Floats(90); break;
   139                 } break;                
   140               case 0x1A: // Intel Core i7 LGA1366 (45nm)
   141               case 0x1E: // Intel Core i5, i7 LGA1156 (45nm)
   142               case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
   143               case 0x2C: // Intel Core i7 LGA1366 (32nm) 6 Core
   144                 uint eax, edx;
   145                 tjMax = new float[coreCount];
   146                 for (int i = 0; i < coreCount; i++) {
   147                   if (WinRing0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax,
   148                     out edx, (UIntPtr)(1L << cpuid[i][0].Thread)))
   149                   {
   150                     tjMax[i] = (eax >> 16) & 0xFF;
   151                   } else {
   152                     tjMax[i] = 100;
   153                   }
   154                 }
   155                 if (WinRing0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) {
   156                   maxNehalemMultiplier = (eax >> 8) & 0xff;
   157                 }
   158                 break;
   159               default:
   160                 tjMax = Floats(100); break;
   161             }
   162           } break;
   163         default: tjMax = Floats(100); break;
   164       }
   165 
   166       // check if processor supports a digital thermal sensor
   167       if (cpuid[0][0].Data.GetLength(0) > 6 && 
   168         (cpuid[0][0].Data[6, 0] & 1) != 0) 
   169       {
   170         coreTemperatures = new Sensor[coreCount];
   171         for (int i = 0; i < coreTemperatures.Length; i++) {
   172           coreTemperatures[i] = new Sensor(CoreString(i), i,
   173             SensorType.Temperature, this, new ParameterDescription[] { 
   174               new ParameterDescription(
   175                 "TjMax [°C]", "TjMax temperature of the core.\n" + 
   176                 "Temperature = TjMax - TSlope * Value.", tjMax[i]), 
   177               new ParameterDescription("TSlope [°C]", 
   178                 "Temperature slope of the digital thermal sensor.\n" + 
   179                 "Temperature = TjMax - TSlope * Value.", 1)}, settings);
   180           ActivateSensor(coreTemperatures[i]);
   181         }
   182       } else {
   183         coreTemperatures = new Sensor[0];
   184       }
   185 
   186       if (coreCount > 1)
   187         totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this, settings);
   188       else
   189         totalLoad = null;
   190       coreLoads = new Sensor[coreCount];
   191       for (int i = 0; i < coreLoads.Length; i++)
   192         coreLoads[i] = new Sensor(CoreString(i), i + 1,
   193           SensorType.Load, this, settings);     
   194       cpuLoad = new CPULoad(cpuid);
   195       if (cpuLoad.IsAvailable) {
   196         foreach (Sensor sensor in coreLoads)
   197           ActivateSensor(sensor);
   198         if (totalLoad != null)
   199           ActivateSensor(totalLoad);
   200       }
   201 
   202       // check if processor has TSC
   203       if (cpuid[0][0].Data.GetLength(0) > 1 
   204         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
   205         hasTSC = true;
   206       else
   207         hasTSC = false; 
   208 
   209       // check if processor supports invariant TSC 
   210       if (cpuid[0][0].ExtData.GetLength(0) > 7 
   211         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
   212         invariantTSC = true;
   213       else
   214         invariantTSC = false;
   215 
   216       // preload the function
   217       EstimateMaxClock(0); 
   218       EstimateMaxClock(0); 
   219 
   220       // estimate the max clock in MHz      
   221       List<double> estimatedMaxClocks = new List<double>(3);
   222       for (int i = 0; i < 3; i++)
   223         estimatedMaxClocks.Add(1e-6 * EstimateMaxClock(0.025));
   224       estimatedMaxClocks.Sort();
   225       estimatedMaxClock = estimatedMaxClocks[1];
   226 
   227       lastTimeStampCount = 0;
   228       lastTime = 0;
   229       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);      
   230       coreClocks = new Sensor[coreCount];
   231       for (int i = 0; i < coreClocks.Length; i++) {
   232         coreClocks[i] =
   233           new Sensor(CoreString(i), i + 1, SensorType.Clock, this, settings);
   234         if (hasTSC)
   235           ActivateSensor(coreClocks[i]);
   236       }
   237       
   238       Update();                   
   239     }
   240 
   241     public override string Name {
   242       get { return name; }
   243     }
   244 
   245     public override Identifier Identifier {
   246       get { 
   247         return new Identifier("intelcpu", 
   248           processorIndex.ToString(CultureInfo.InvariantCulture)); 
   249       }
   250     }
   251 
   252     public override HardwareType HardwareType {
   253       get { return HardwareType.CPU; }
   254     }
   255 
   256     private static void AppendMSRData(StringBuilder r, uint msr, int thread) {
   257       uint eax, edx;
   258       if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
   259         r.Append(" ");
   260         r.Append((msr).ToString("X8", CultureInfo.InvariantCulture));
   261         r.Append("  ");
   262         r.Append((edx).ToString("X8", CultureInfo.InvariantCulture));
   263         r.Append("  ");
   264         r.Append((eax).ToString("X8", CultureInfo.InvariantCulture));
   265         r.AppendLine();
   266       }
   267     }
   268 
   269     public override string GetReport() {
   270       StringBuilder r = new StringBuilder();
   271 
   272       r.AppendLine("Intel CPU");
   273       r.AppendLine();
   274       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   275       r.AppendFormat("Number of Cores: {0}{1}", coreCount, 
   276         Environment.NewLine);
   277       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   278         Environment.NewLine);     
   279       r.AppendLine("TSC: " + 
   280         (hasTSC ? (invariantTSC ? "Invariant" : "Not Invariant") : "None"));
   281       r.AppendLine(string.Format(CultureInfo.InvariantCulture, 
   282         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   283       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   284         "Max Clock: {0} MHz", Math.Round(estimatedMaxClock * 100) * 0.01));
   285       r.AppendLine();
   286 
   287       for (int i = 0; i < cpuid.Length; i++) {
   288         r.AppendLine("MSR Core #" + (i + 1));
   289         r.AppendLine();
   290         r.AppendLine(" MSR       EDX       EAX");
   291         AppendMSRData(r, MSR_PLATFORM_INFO, cpuid[i][0].Thread);
   292         AppendMSRData(r, IA32_PERF_STATUS, cpuid[i][0].Thread);
   293         AppendMSRData(r, IA32_THERM_STATUS_MSR, cpuid[i][0].Thread);
   294         AppendMSRData(r, IA32_TEMPERATURE_TARGET, cpuid[i][0].Thread);
   295         r.AppendLine();
   296       }
   297 
   298       return r.ToString();
   299     }
   300 
   301     private static double EstimateMaxClock(double timeWindow) {
   302       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   303       uint lsbBegin, msbBegin, lsbEnd, msbEnd; 
   304       
   305       Thread.BeginThreadAffinity();
   306       long timeBegin = Stopwatch.GetTimestamp() + 
   307         (long)Math.Ceiling(0.001 * ticks);
   308       long timeEnd = timeBegin + ticks;      
   309       while (Stopwatch.GetTimestamp() < timeBegin) { }
   310       WinRing0.Rdtsc(out lsbBegin, out msbBegin);
   311       while (Stopwatch.GetTimestamp() < timeEnd) { }
   312       WinRing0.Rdtsc(out lsbEnd, out msbEnd);
   313       Thread.EndThreadAffinity();
   314 
   315       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   316       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   317 
   318       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / 
   319         (timeEnd - timeBegin);
   320     }
   321 
   322     public override void Update() {      
   323       for (int i = 0; i < coreTemperatures.Length; i++) {
   324         uint eax, edx;
   325         if (WinRing0.RdmsrTx(
   326           IA32_THERM_STATUS_MSR, out eax, out edx, 
   327             (UIntPtr)(1L << cpuid[i][0].Thread))) {
   328           // if reading is valid
   329           if ((eax & 0x80000000) != 0) {
   330             // get the dist from tjMax from bits 22:16
   331             float deltaT = ((eax & 0x007F0000) >> 16);
   332             float tjMax = coreTemperatures[i].Parameters[0].Value;
   333             float tSlope = coreTemperatures[i].Parameters[1].Value;
   334             coreTemperatures[i].Value = tjMax - tSlope * deltaT;
   335           } else {
   336             coreTemperatures[i].Value = null;
   337           }
   338         }
   339       }
   340 
   341       if (cpuLoad.IsAvailable) {
   342         cpuLoad.Update();
   343         for (int i = 0; i < coreLoads.Length; i++)
   344           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   345         if (totalLoad != null)
   346           totalLoad.Value = cpuLoad.GetTotalLoad();
   347       }
   348 
   349       if (hasTSC) {
   350         uint lsb, msb;
   351         WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
   352         long time = Stopwatch.GetTimestamp();
   353         ulong timeStampCount = ((ulong)msb << 32) | lsb;
   354         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   355         if (delta > 0.5) {
   356           double maxClock;
   357           if (invariantTSC)
   358             maxClock = (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   359           else
   360             maxClock = estimatedMaxClock;
   361 
   362           double newBusClock = 0;
   363           uint eax, edx;
   364           for (int i = 0; i < coreClocks.Length; i++) {
   365             System.Threading.Thread.Sleep(1);
   366             if (WinRing0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx,
   367               (UIntPtr)(1L << cpuid[i][0].Thread))) {
   368               if (maxNehalemMultiplier > 0) { // Core i3, i5, i7
   369                 uint nehalemMultiplier = eax & 0xff;
   370                 coreClocks[i].Value =
   371                   (float)(nehalemMultiplier * maxClock / maxNehalemMultiplier);
   372                 newBusClock = (float)(maxClock / maxNehalemMultiplier);
   373               } else { // Core 2
   374                 uint multiplier = (eax >> 8) & 0x1f;
   375                 uint maxMultiplier = (edx >> 8) & 0x1f;
   376                 // factor = multiplier * 2 to handle non integer multipliers 
   377                 uint factor = (multiplier << 1) | ((eax >> 14) & 1);
   378                 uint maxFactor = (maxMultiplier << 1) | ((edx >> 14) & 1);
   379                 if (maxFactor > 0) {
   380                   coreClocks[i].Value = (float)(factor * maxClock / maxFactor);
   381                   newBusClock = (float)(2 * maxClock / maxFactor);
   382                 }
   383               }
   384             } else { // Intel Pentium 4
   385               // if IA32_PERF_STATUS is not available, assume maxClock
   386               coreClocks[i].Value = (float)maxClock;
   387             }
   388           }
   389           if (newBusClock > 0) {
   390             this.busClock.Value = (float)newBusClock;
   391             ActivateSensor(this.busClock);
   392           }
   393         }
   394         lastTimeStampCount = timeStampCount;
   395         lastTime = time;
   396       }
   397     }
   398   }  
   399 }