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