Hardware/CPU/IntelCPU.cs
author moel.mich
Sat, 24 Apr 2010 19:59:52 +0000
changeset 90 3333b29a1746
parent 86 b4f0f206173d
child 91 c0937b698b81
permissions -rw-r--r--
Implemented APIC based CPU enumeration (Issue 41).
     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.Drawing;
    41 using System.Diagnostics;
    42 using System.Globalization;
    43 using System.Reflection;
    44 using System.Runtime.InteropServices;
    45 using System.Threading;
    46 using System.Text;
    47 
    48 namespace OpenHardwareMonitor.Hardware.CPU {
    49   public class IntelCPU : Hardware, IHardware {
    50 
    51     private CPUID[][] cpuid;
    52     private int coreCount;
    53     
    54     private string name;
    55     private Image icon;
    56 
    57     private uint family;
    58     private uint model;
    59     private uint stepping;
    60 
    61     private Sensor[] coreTemperatures;
    62 
    63     private Sensor totalLoad;
    64     private Sensor[] coreLoads;
    65     private Sensor[] coreClocks;
    66     private Sensor busClock;    
    67     private bool hasTSC;
    68     private bool invariantTSC;    
    69     private double estimatedMaxClock;
    70 
    71     private CPULoad cpuLoad;
    72 
    73     private ulong lastTimeStampCount;    
    74     private long lastTime;
    75     private uint maxNehalemMultiplier = 0;    
    76     
    77     private const uint IA32_THERM_STATUS_MSR = 0x019C;
    78     private const uint IA32_TEMPERATURE_TARGET = 0x01A2;
    79     private const uint IA32_PERF_STATUS = 0x0198;
    80     private const uint MSR_PLATFORM_INFO = 0xCE;
    81 
    82     private string CoreString(int i) {
    83       if (coreCount == 1)
    84         return "CPU Core";
    85       else
    86         return "CPU Core #" + (i + 1);
    87     }
    88 
    89     private float[] Floats(float f) {
    90       float[] result = new float[coreCount];
    91       for (int i = 0; i < coreCount; i++)
    92         result[i] = f;
    93       return result;
    94     }
    95 
    96     public IntelCPU(CPUID[][] cpuid) {
    97 
    98       this.cpuid = cpuid;
    99       this.coreCount = cpuid.Length;
   100       this.name = cpuid[0][0].Name;
   101       this.icon = Utilities.EmbeddedResources.GetImage("cpu.png");
   102 
   103       this.family = cpuid[0][0].Family;
   104       this.model = cpuid[0][0].Model;
   105       this.stepping = cpuid[0][0].Stepping;
   106 
   107       float[] tjMax;
   108       switch (family) {
   109         case 0x06: {
   110             switch (model) {
   111               case 0x0F: // Intel Core (65nm)
   112                 switch (stepping) {
   113                   case 0x06: // B2
   114                     switch (coreCount) {
   115                       case 2:
   116                         tjMax = Floats(80 + 10); break;
   117                       case 4:
   118                         tjMax = Floats(90 + 10); break;
   119                       default:
   120                         tjMax = Floats(85 + 10); break;
   121                     }
   122                     tjMax = Floats(80 + 10); break;
   123                   case 0x0B: // G0
   124                     tjMax = Floats(90 + 10); break;
   125                   case 0x0D: // M0
   126                     tjMax = Floats(85 + 10); break;
   127                   default:
   128                     tjMax = Floats(85 + 10); break;
   129                 } break;
   130               case 0x17: // Intel Core (45nm)
   131                 tjMax = Floats(100); break;
   132               case 0x1C: // Intel Atom 
   133                 tjMax = Floats(90); break;
   134               case 0x1A: // Intel Core i7 LGA1366 (45nm)
   135               case 0x1E: // Intel Core i5, i7 LGA1156 (45nm)
   136               case 0x25: // Intel Core i3, i5, i7 LGA1156 (32nm)
   137                 uint eax, edx;
   138                 tjMax = new float[coreCount];
   139                 for (int i = 0; i < coreCount; i++) {
   140                   if (WinRing0.RdmsrTx(IA32_TEMPERATURE_TARGET, out eax,
   141                     out edx, (UIntPtr)(1L << cpuid[i][0].Thread)))
   142                   {
   143                     tjMax[i] = (eax >> 16) & 0xFF;
   144                   } else {
   145                     tjMax[i] = 100;
   146                   }
   147                 }
   148                 if (WinRing0.Rdmsr(MSR_PLATFORM_INFO, out eax, out edx)) {
   149                   maxNehalemMultiplier = (eax >> 8) & 0xff;
   150                 }
   151                 break;
   152               default:
   153                 tjMax = Floats(100); break;
   154             }
   155           } break;
   156         default: tjMax = Floats(100); break;
   157       }
   158 
   159       // check if processor supports a digital thermal sensor
   160       if (cpuid[0][0].Data.GetLength(0) > 6 && 
   161         (cpuid[0][0].Data[6, 0] & 1) != 0) 
   162       {
   163         coreTemperatures = new Sensor[coreCount];
   164         for (int i = 0; i < coreTemperatures.Length; i++) {
   165           coreTemperatures[i] = new Sensor(CoreString(i), i, tjMax[i],
   166             SensorType.Temperature, this, new ParameterDescription[] { 
   167               new ParameterDescription(
   168                 "TjMax", "TjMax temperature of the core.\n" + 
   169                 "Temperature = TjMax - TSlope * Value.", tjMax[i]), 
   170               new ParameterDescription(
   171                 "TSlope", "Temperature slope of the digital thermal sensor.\n" + 
   172                 "Temperature = TjMax - TSlope * Value.", 1)});
   173         }
   174       } else {
   175         coreTemperatures = new Sensor[0];
   176       }
   177 
   178       if (coreCount > 1)
   179         totalLoad = new Sensor("CPU Total", 0, SensorType.Load, this);
   180       else
   181         totalLoad = null;
   182       coreLoads = new Sensor[coreCount];
   183       for (int i = 0; i < coreLoads.Length; i++)
   184         coreLoads[i] = new Sensor(CoreString(i), i + 1,
   185           SensorType.Load, this);     
   186       cpuLoad = new CPULoad(cpuid);
   187       if (cpuLoad.IsAvailable) {
   188         foreach (Sensor sensor in coreLoads)
   189           ActivateSensor(sensor);
   190         if (totalLoad != null)
   191           ActivateSensor(totalLoad);
   192       }
   193 
   194       // check if processor has TSC
   195       if (cpuid[0][0].Data.GetLength(0) > 1 
   196         && (cpuid[0][0].Data[1, 3] & 0x10) != 0)
   197         hasTSC = true;
   198       else
   199         hasTSC = false; 
   200 
   201       // check if processor supports invariant TSC 
   202       if (cpuid[0][0].ExtData.GetLength(0) > 7 
   203         && (cpuid[0][0].ExtData[7, 3] & 0x100) != 0)
   204         invariantTSC = true;
   205       else
   206         invariantTSC = false;
   207 
   208       // preload the function
   209       EstimateMaxClock(0); 
   210       EstimateMaxClock(0); 
   211 
   212       // estimate the max clock in MHz      
   213       estimatedMaxClock = 1e-6 * EstimateMaxClock(0.01);
   214 
   215       lastTimeStampCount = 0;
   216       lastTime = 0;
   217       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this);      
   218       coreClocks = new Sensor[coreCount];
   219       for (int i = 0; i < coreClocks.Length; i++) {
   220         coreClocks[i] =
   221           new Sensor(CoreString(i), i + 1, SensorType.Clock, this);
   222         if (hasTSC)
   223           ActivateSensor(coreClocks[i]);
   224       }
   225       
   226       Update();                   
   227     }
   228 
   229     public string Name {
   230       get { return name; }
   231     }
   232 
   233     public string Identifier {
   234       get { return "/intelcpu/0"; }
   235     }
   236 
   237     public Image Icon {
   238       get { return icon; }
   239     }
   240 
   241     private void AppendMSRData(StringBuilder r, uint msr, int thread) {
   242       uint eax, edx;
   243       if (WinRing0.RdmsrTx(msr, out eax, out edx, (UIntPtr)(1L << thread))) {
   244         r.Append(" ");
   245         r.Append((msr).ToString("X8"));
   246         r.Append("  ");
   247         r.Append((edx).ToString("X8"));
   248         r.Append("  ");
   249         r.Append((eax).ToString("X8"));
   250         r.AppendLine();
   251       }
   252     }
   253 
   254     public string GetReport() {
   255       StringBuilder r = new StringBuilder();
   256 
   257       r.AppendLine("Intel CPU");
   258       r.AppendLine();
   259       r.AppendFormat("Name: {0}{1}", name, Environment.NewLine);
   260       r.AppendFormat("Number of Cores: {0}{1}", coreCount, 
   261         Environment.NewLine);
   262       r.AppendFormat("Threads per Core: {0}{1}", cpuid[0].Length,
   263         Environment.NewLine);     
   264       r.AppendLine("TSC: " + 
   265         (hasTSC ? (invariantTSC ? "Invariant" : "Not Invariant") : "None"));
   266       r.AppendLine(string.Format(CultureInfo.InvariantCulture, 
   267         "Timer Frequency: {0} MHz", Stopwatch.Frequency * 1e-6));
   268       r.AppendLine(string.Format(CultureInfo.InvariantCulture,
   269         "Max Clock: {0} MHz", Math.Round(estimatedMaxClock * 100) * 0.01));
   270       r.AppendLine();
   271 
   272       for (int i = 0; i < cpuid.Length; i++) {
   273         r.AppendLine("MSR Core #" + (i + 1));
   274         r.AppendLine();
   275         r.AppendLine(" MSR       EDX       EAX");
   276         AppendMSRData(r, MSR_PLATFORM_INFO, cpuid[i][0].Thread);
   277         AppendMSRData(r, IA32_PERF_STATUS, cpuid[i][0].Thread);
   278         AppendMSRData(r, IA32_THERM_STATUS_MSR, cpuid[i][0].Thread);
   279         AppendMSRData(r, IA32_TEMPERATURE_TARGET, cpuid[i][0].Thread);
   280         r.AppendLine();
   281       }
   282 
   283       return r.ToString();
   284     }
   285 
   286     private double EstimateMaxClock(double timeWindow) {
   287       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   288       uint lsbBegin, msbBegin, lsbEnd, msbEnd; 
   289       
   290       Thread.BeginThreadAffinity();
   291       long timeBegin = Stopwatch.GetTimestamp() + 2;
   292       long timeEnd = timeBegin + ticks;      
   293       while (Stopwatch.GetTimestamp() < timeBegin) { }
   294       WinRing0.Rdtsc(out lsbBegin, out msbBegin);
   295       while (Stopwatch.GetTimestamp() < timeEnd) { }
   296       WinRing0.Rdtsc(out lsbEnd, out msbEnd);
   297       Thread.EndThreadAffinity();
   298 
   299       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   300       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   301 
   302       return (((double)(countEnd - countBegin)) * Stopwatch.Frequency) / 
   303         (timeEnd - timeBegin);
   304     }
   305 
   306     public void Update() {      
   307       for (int i = 0; i < coreTemperatures.Length; i++) {
   308         uint eax, edx;
   309         if (WinRing0.RdmsrTx(
   310           IA32_THERM_STATUS_MSR, out eax, out edx, 
   311             (UIntPtr)(1L << cpuid[i][0].Thread))) {
   312           // if reading is valid
   313           if ((eax & 0x80000000) != 0) {
   314             // get the dist from tjMax from bits 22:16
   315             float deltaT = ((eax & 0x007F0000) >> 16);
   316             float tjMax = coreTemperatures[i].Parameters[0].Value;
   317             float tSlope = coreTemperatures[i].Parameters[1].Value;
   318             coreTemperatures[i].Value = tjMax - tSlope * deltaT;
   319             ActivateSensor(coreTemperatures[i]);
   320           } else {
   321             DeactivateSensor(coreTemperatures[i]);
   322           }
   323         }
   324       }
   325 
   326       if (cpuLoad.IsAvailable) {
   327         cpuLoad.Update();
   328         for (int i = 0; i < coreLoads.Length; i++)
   329           coreLoads[i].Value = cpuLoad.GetCoreLoad(i);
   330         if (totalLoad != null)
   331           totalLoad.Value = cpuLoad.GetTotalLoad();
   332       }
   333 
   334       if (hasTSC) {
   335         uint lsb, msb;
   336         WinRing0.RdtscTx(out lsb, out msb, (UIntPtr)1);
   337         long time = Stopwatch.GetTimestamp();
   338         ulong timeStampCount = ((ulong)msb << 32) | lsb;
   339         double delta = ((double)(time - lastTime)) / Stopwatch.Frequency;
   340         if (delta > 0.5) {
   341           double maxClock;
   342           if (invariantTSC)
   343             maxClock = (timeStampCount - lastTimeStampCount) / (1e6 * delta);
   344           else
   345             maxClock = estimatedMaxClock;
   346 
   347           double busClock = 0;
   348           uint eax, edx;
   349           for (int i = 0; i < coreClocks.Length; i++) {
   350             System.Threading.Thread.Sleep(1);
   351             if (WinRing0.RdmsrTx(IA32_PERF_STATUS, out eax, out edx,
   352               (UIntPtr)(1L << cpuid[i][0].Thread))) {
   353               if (maxNehalemMultiplier > 0) { // Core i3, i5, i7
   354                 uint nehalemMultiplier = eax & 0xff;
   355                 coreClocks[i].Value =
   356                   (float)(nehalemMultiplier * maxClock / maxNehalemMultiplier);
   357                 busClock = (float)(maxClock / maxNehalemMultiplier);
   358               } else { // Core 2
   359                 uint multiplier = (eax >> 8) & 0x1f;
   360                 uint maxMultiplier = (edx >> 8) & 0x1f;
   361                 // factor = multiplier * 2 to handle non integer multipliers 
   362                 uint factor = (multiplier << 1) | ((eax >> 14) & 1);
   363                 uint maxFactor = (maxMultiplier << 1) | ((edx >> 14) & 1);
   364                 if (maxFactor > 0) {
   365                   coreClocks[i].Value = (float)(factor * maxClock / maxFactor);
   366                   busClock = (float)(2 * maxClock / maxFactor);
   367                 }
   368               }
   369             } else { // Intel Pentium 4
   370               // if IA32_PERF_STATUS is not available, assume maxClock
   371               coreClocks[i].Value = (float)maxClock;
   372             }
   373           }
   374           if (busClock > 0) {
   375             this.busClock.Value = (float)busClock;
   376             ActivateSensor(this.busClock);
   377           }
   378         }
   379         lastTimeStampCount = timeStampCount;
   380         lastTime = time;
   381       }
   382     }
   383   }  
   384 }