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