Hardware/CPU/AMD10CPU.cs
author moel.mich
Mon, 13 Feb 2012 21:56:29 +0000
changeset 339 07a6126a4796
parent 301 d14ce71cef44
child 344 3145aadca3d2
permissions -rw-r--r--
Added additional smart attribute identification and a write amplification sensor for Sandforce based SSDs.
     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-2011
    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.IO;
    43 using System.Runtime.InteropServices;
    44 using System.Text;
    45 using System.Threading;
    46 
    47 namespace OpenHardwareMonitor.Hardware.CPU {
    48 
    49   internal sealed class AMD10CPU : AMDCPU {
    50 
    51     private readonly Sensor coreTemperature;
    52     private readonly Sensor[] coreClocks;
    53     private readonly Sensor busClock;
    54       
    55     private const uint PERF_CTL_0 = 0xC0010000;
    56     private const uint PERF_CTR_0 = 0xC0010004;
    57     private const uint HWCR = 0xC0010015;
    58     private const uint P_STATE_0 = 0xC0010064;
    59     private const uint COFVID_STATUS = 0xC0010071;
    60 
    61     private const byte MISCELLANEOUS_CONTROL_FUNCTION = 3;
    62     private const ushort FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1203;
    63     private const ushort FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1303;
    64     private const ushort FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
    65     private const ushort FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1703;
    66     private const ushort FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID = 0x1603; 
    67     private const uint REPORTED_TEMPERATURE_CONTROL_REGISTER = 0xA4;
    68     private const uint CLOCK_POWER_TIMING_CONTROL_0_REGISTER = 0xD4;
    69 
    70     private readonly uint miscellaneousControlAddress;
    71     private readonly ushort miscellaneousControlDeviceId;
    72 
    73     private readonly FileStream temperatureStream;
    74 
    75     private readonly double timeStampCounterMultiplier;
    76     private readonly bool corePerformanceBoostSupport;
    77 
    78     public AMD10CPU(int processorIndex, CPUID[][] cpuid, ISettings settings)
    79       : base(processorIndex, cpuid, settings) 
    80     {            
    81       // AMD family 1Xh processors support only one temperature sensor
    82       coreTemperature = new Sensor(
    83         "Core" + (coreCount > 1 ? " #1 - #" + coreCount : ""), 0,
    84         SensorType.Temperature, this, new [] {
    85             new ParameterDescription("Offset [°C]", "Temperature offset.", 0)
    86           }, settings);
    87 
    88       switch (family) {
    89         case 0x10: miscellaneousControlDeviceId =
    90           FAMILY_10H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    91         case 0x11: miscellaneousControlDeviceId =
    92           FAMILY_11H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    93         case 0x12: miscellaneousControlDeviceId =
    94           FAMILY_12H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    95         case 0x14: miscellaneousControlDeviceId = 
    96           FAMILY_14H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    97         case 0x15: miscellaneousControlDeviceId =
    98           FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID; break;
    99         default: miscellaneousControlDeviceId = 0; break;
   100       }
   101 
   102       // get the pci address for the Miscellaneous Control registers 
   103       miscellaneousControlAddress = GetPciAddress(
   104         MISCELLANEOUS_CONTROL_FUNCTION, miscellaneousControlDeviceId);        
   105 
   106       busClock = new Sensor("Bus Speed", 0, SensorType.Clock, this, settings);
   107       coreClocks = new Sensor[coreCount];
   108       for (int i = 0; i < coreClocks.Length; i++) {
   109         coreClocks[i] = new Sensor(CoreString(i), i + 1, SensorType.Clock,
   110           this, settings);
   111         if (HasTimeStampCounter)
   112           ActivateSensor(coreClocks[i]);
   113       }
   114 
   115       corePerformanceBoostSupport = (cpuid[0][0].ExtData[7, 3] & (1 << 9)) > 0;
   116 
   117       // set affinity to the first thread for all frequency estimations     
   118       ulong mask = ThreadAffinity.Set(1UL << cpuid[0][0].Thread);
   119 
   120       // disable core performance boost  
   121       uint hwcrEax, hwcrEdx;
   122       Ring0.Rdmsr(HWCR, out hwcrEax, out hwcrEdx);
   123       if (corePerformanceBoostSupport) 
   124         Ring0.Wrmsr(HWCR, hwcrEax | (1 << 25), hwcrEdx);
   125 
   126       uint ctlEax, ctlEdx;
   127       Ring0.Rdmsr(PERF_CTL_0, out ctlEax, out ctlEdx);
   128       uint ctrEax, ctrEdx;
   129       Ring0.Rdmsr(PERF_CTR_0, out ctrEax, out ctrEdx);
   130 
   131       timeStampCounterMultiplier = estimateTimeStampCounterMultiplier();
   132 
   133       // restore the performance counter registers
   134       Ring0.Wrmsr(PERF_CTL_0, ctlEax, ctlEdx);
   135       Ring0.Wrmsr(PERF_CTR_0, ctrEax, ctrEdx);
   136 
   137       // restore core performance boost
   138       if (corePerformanceBoostSupport)     
   139         Ring0.Wrmsr(HWCR, hwcrEax, hwcrEdx);
   140 
   141       // restore the thread affinity.
   142       ThreadAffinity.Set(mask);
   143 
   144       // the file reader for lm-sensors support on Linux
   145       temperatureStream = null;
   146       int p = (int)Environment.OSVersion.Platform;
   147       if ((p == 4) || (p == 128)) {
   148         string[] devicePaths = Directory.GetDirectories("/sys/class/hwmon/");
   149         foreach (string path in devicePaths) {
   150           string name = null;
   151           try {
   152             using (StreamReader reader = new StreamReader(path + "/device/name"))
   153               name = reader.ReadLine();
   154           } catch (IOException) { }
   155           switch (name) {
   156             case "k10temp":
   157               temperatureStream = new FileStream(path + "/device/temp1_input", 
   158                 FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
   159               break;
   160           }
   161         }
   162       }
   163 
   164       Update();                   
   165     }
   166 
   167     private double estimateTimeStampCounterMultiplier() {
   168       // preload the function
   169       estimateTimeStampCounterMultiplier(0);
   170       estimateTimeStampCounterMultiplier(0);
   171 
   172       // estimate the multiplier
   173       List<double> estimate = new List<double>(3);
   174       for (int i = 0; i < 3; i++)
   175         estimate.Add(estimateTimeStampCounterMultiplier(0.025));
   176       estimate.Sort();
   177       return estimate[1];
   178     }
   179 
   180     private double estimateTimeStampCounterMultiplier(double timeWindow) {
   181       uint eax, edx;
   182      
   183       // select event "076h CPU Clocks not Halted" and enable the counter
   184       Ring0.Wrmsr(PERF_CTL_0,
   185         (1 << 22) | // enable performance counter
   186         (1 << 17) | // count events in user mode
   187         (1 << 16) | // count events in operating-system mode
   188         0x76, 0x00000000);
   189 
   190       // set the counter to 0
   191       Ring0.Wrmsr(PERF_CTR_0, 0, 0);
   192 
   193       long ticks = (long)(timeWindow * Stopwatch.Frequency);
   194       uint lsbBegin, msbBegin, lsbEnd, msbEnd;      
   195 
   196       long timeBegin = Stopwatch.GetTimestamp() +
   197         (long)Math.Ceiling(0.001 * ticks);
   198       long timeEnd = timeBegin + ticks;
   199       while (Stopwatch.GetTimestamp() < timeBegin) { }
   200       Ring0.Rdmsr(PERF_CTR_0, out lsbBegin, out msbBegin);
   201 
   202       while (Stopwatch.GetTimestamp() < timeEnd) { }
   203       Ring0.Rdmsr(PERF_CTR_0, out lsbEnd, out msbEnd);
   204       Ring0.Rdmsr(COFVID_STATUS, out eax, out edx);
   205       double coreMultiplier = GetCoreMultiplier(eax);
   206 
   207       ulong countBegin = ((ulong)msbBegin << 32) | lsbBegin;
   208       ulong countEnd = ((ulong)msbEnd << 32) | lsbEnd;
   209 
   210       double coreFrequency = 1e-6 * 
   211         (((double)(countEnd - countBegin)) * Stopwatch.Frequency) /
   212         (timeEnd - timeBegin);
   213 
   214       double busFrequency = coreFrequency / coreMultiplier;
   215 
   216       return 0.25 * Math.Round(4 * TimeStampCounterFrequency / busFrequency);
   217     }
   218 
   219     protected override uint[] GetMSRs() {
   220       return new uint[] { PERF_CTL_0, PERF_CTR_0, HWCR, P_STATE_0, 
   221         COFVID_STATUS };
   222     }
   223 
   224     public override string GetReport() {
   225       StringBuilder r = new StringBuilder();
   226       r.Append(base.GetReport());
   227 
   228       r.Append("Miscellaneous Control Address: 0x");
   229       r.AppendLine((miscellaneousControlAddress).ToString("X",
   230         CultureInfo.InvariantCulture));
   231       r.Append("Time Stamp Counter Multiplier: ");
   232       r.AppendLine(timeStampCounterMultiplier.ToString(
   233         CultureInfo.InvariantCulture));
   234       if (family == 0x14) {
   235         uint value = 0;
   236         Ring0.ReadPciConfig(miscellaneousControlAddress,
   237           CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
   238         r.Append("PCI Register D18F3xD4: ");
   239         r.AppendLine(value.ToString("X8", CultureInfo.InvariantCulture));
   240       }
   241       r.AppendLine();
   242 
   243       return r.ToString();
   244     }
   245 
   246     private double GetCoreMultiplier(uint cofvidEax) {
   247       switch (family) {
   248         case 0x10:
   249         case 0x11: 
   250         case 0x15: {
   251             // 8:6 CpuDid: current core divisor ID
   252             // 5:0 CpuFid: current core frequency ID
   253             uint cpuDid = (cofvidEax >> 6) & 7;
   254             uint cpuFid = cofvidEax & 0x1F;
   255             return 0.5 * (cpuFid + 0x10) / (1 << (int)cpuDid);
   256           }
   257         case 0x12: {
   258             // 8:4 CpuFid: current CPU core frequency ID
   259             // 3:0 CpuDid: current CPU core divisor ID
   260             uint cpuFid = (cofvidEax >> 4) & 0x1F;
   261             uint cpuDid = cofvidEax & 0xF;
   262             double divisor;
   263             switch (cpuDid) {
   264               case 0: divisor = 1; break;
   265               case 1: divisor = 1.5; break;
   266               case 2: divisor = 2; break;
   267               case 3: divisor = 3; break;
   268               case 4: divisor = 4; break;
   269               case 5: divisor = 6; break;
   270               case 6: divisor = 8; break;
   271               case 7: divisor = 12; break;
   272               case 8: divisor = 16; break;
   273               default: divisor = 1; break;
   274             }
   275             return (cpuFid + 0x10) / divisor;
   276           }
   277         case 0x14: {
   278             // 8:4: current CPU core divisor ID most significant digit
   279             // 3:0: current CPU core divisor ID least significant digit
   280             uint divisorIdMSD = (cofvidEax >> 4) & 0x1F;
   281             uint divisorIdLSD = cofvidEax & 0xF;
   282             uint value = 0;
   283             Ring0.ReadPciConfig(miscellaneousControlAddress,
   284               CLOCK_POWER_TIMING_CONTROL_0_REGISTER, out value);
   285             uint frequencyId = value & 0x1F;
   286             return (frequencyId + 0x10) /
   287               (divisorIdMSD + (divisorIdLSD * 0.25) + 1);
   288           }
   289         default:
   290           return 1;
   291       }
   292     }
   293 
   294     private string ReadFirstLine(Stream stream) {
   295       StringBuilder sb = new StringBuilder();
   296       try {
   297         stream.Seek(0, SeekOrigin.Begin);
   298         int b = stream.ReadByte();
   299         while (b != -1 && b != 10) {
   300           sb.Append((char)b);
   301           b = stream.ReadByte();
   302         }
   303       } catch { }
   304       return sb.ToString();
   305     }
   306 
   307     public override void Update() {
   308       base.Update();
   309 
   310       if (temperatureStream == null) {
   311         if (miscellaneousControlAddress != Ring0.InvalidPciAddress) {
   312           uint value;
   313           if (Ring0.ReadPciConfig(miscellaneousControlAddress,
   314             REPORTED_TEMPERATURE_CONTROL_REGISTER, out value)) {
   315             if (family == 0x15 && (value & 0x30000) == 0x30000) {
   316               coreTemperature.Value = ((value >> 21) & 0x7FC) / 8.0f +
   317                 coreTemperature.Parameters[0].Value - 49;
   318             } else {
   319               coreTemperature.Value = ((value >> 21) & 0x7FF) / 8.0f +
   320                 coreTemperature.Parameters[0].Value;
   321             }
   322             ActivateSensor(coreTemperature);
   323           } else {
   324             DeactivateSensor(coreTemperature);
   325           }
   326         }
   327       } else {
   328         string s = ReadFirstLine(temperatureStream);
   329         try {
   330           coreTemperature.Value = 0.001f *
   331             long.Parse(s, CultureInfo.InvariantCulture);
   332           ActivateSensor(coreTemperature);
   333         } catch {
   334           DeactivateSensor(coreTemperature);
   335         }        
   336       }
   337 
   338       if (HasTimeStampCounter) {
   339         double newBusClock = 0;
   340 
   341         for (int i = 0; i < coreClocks.Length; i++) {
   342           Thread.Sleep(1);
   343 
   344           uint curEax, curEdx;
   345           if (Ring0.RdmsrTx(COFVID_STATUS, out curEax, out curEdx,
   346             1UL << cpuid[i][0].Thread)) 
   347           {
   348             double multiplier;
   349             multiplier = GetCoreMultiplier(curEax);
   350 
   351             coreClocks[i].Value = 
   352               (float)(multiplier * TimeStampCounterFrequency / 
   353               timeStampCounterMultiplier);
   354             newBusClock = 
   355               (float)(TimeStampCounterFrequency / timeStampCounterMultiplier);
   356           } else {
   357             coreClocks[i].Value = (float)TimeStampCounterFrequency;
   358           }
   359         }
   360 
   361         if (newBusClock > 0) {
   362           this.busClock.Value = (float)newBusClock;
   363           ActivateSensor(this.busClock);
   364         }
   365       }
   366     }
   367 
   368     public override void Close() {      
   369       if (temperatureStream != null) {
   370         temperatureStream.Close();
   371       }
   372       base.Close();
   373     }
   374   }
   375 }