/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Copyright (C) 2009-2014 Michael Möller */ using System; using System.Text; namespace OpenHardwareMonitor.Hardware.CPU { public enum Vendor { Unknown, Intel, AMD } public class CPUID { public const uint CPUID_0 = 0; public const uint CPUID_EXT = 0x80000000; private readonly uint coreMaskWith; private readonly uint threadMaskWith; public CPUID(int thread) { Thread = thread; uint maxCpuidExt; uint eax, ebx, ecx, edx; if (thread >= 64) throw new ArgumentOutOfRangeException("thread"); ulong mask = 1UL << thread; uint maxCpuid; if (Opcode.CpuidTx(CPUID_0, 0, out eax, out ebx, out ecx, out edx, mask)) { if (eax > 0) maxCpuid = eax; else return; StringBuilder vendorBuilder = new StringBuilder(); AppendRegister(vendorBuilder, ebx); AppendRegister(vendorBuilder, edx); AppendRegister(vendorBuilder, ecx); string cpuVendor = vendorBuilder.ToString(); switch (cpuVendor) { case "GenuineIntel": Vendor = Vendor.Intel; break; case "AuthenticAMD": Vendor = Vendor.AMD; break; default: Vendor = Vendor.Unknown; break; } eax = ebx = ecx = edx = 0; if (Opcode.CpuidTx(CPUID_EXT, 0, out eax, out ebx, out ecx, out edx, mask)) { if (eax > CPUID_EXT) maxCpuidExt = eax - CPUID_EXT; else return; } else { throw new ArgumentOutOfRangeException("thread"); } } else { throw new ArgumentOutOfRangeException("thread"); } maxCpuid = Math.Min(maxCpuid, 1024); maxCpuidExt = Math.Min(maxCpuidExt, 1024); Data = new uint[maxCpuid + 1, 4]; for (uint i = 0; i < (maxCpuid + 1); i++) Opcode.CpuidTx(CPUID_0 + i, 0, out Data[i, 0], out Data[i, 1], out Data[i, 2], out Data[i, 3], mask); ExtData = new uint[maxCpuidExt + 1, 4]; for (uint i = 0; i < (maxCpuidExt + 1); i++) Opcode.CpuidTx(CPUID_EXT + i, 0, out ExtData[i, 0], out ExtData[i, 1], out ExtData[i, 2], out ExtData[i, 3], mask); StringBuilder nameBuilder = new StringBuilder(); for (uint i = 2; i <= 4; i++) { if (Opcode.CpuidTx(CPUID_EXT + i, 0, out eax, out ebx, out ecx, out edx, mask)) { AppendRegister(nameBuilder, eax); AppendRegister(nameBuilder, ebx); AppendRegister(nameBuilder, ecx); AppendRegister(nameBuilder, edx); } } nameBuilder.Replace('\0', ' '); BrandString = nameBuilder.ToString().Trim(); nameBuilder.Replace("(R)", " "); nameBuilder.Replace("(TM)", " "); nameBuilder.Replace("(tm)", ""); nameBuilder.Replace("CPU", ""); nameBuilder.Replace("Quad-Core Processor", ""); nameBuilder.Replace("Six-Core Processor", ""); nameBuilder.Replace("Eight-Core Processor", ""); for (int i = 0; i < 10; i++) nameBuilder.Replace(" ", " "); Name = nameBuilder.ToString(); if (Name.Contains("@")) Name = Name.Remove(Name.LastIndexOf('@')); Name = Name.Trim(); Family = ((Data[1, 0] & 0x0FF00000) >> 20) + ((Data[1, 0] & 0x0F00) >> 8); Model = ((Data[1, 0] & 0x0F0000) >> 12) + ((Data[1, 0] & 0xF0) >> 4); Stepping = (Data[1, 0] & 0x0F); ApicId = (Data[1, 1] >> 24) & 0xFF; switch (Vendor) { case Vendor.Intel: uint maxCoreAndThreadIdPerPackage = (Data[1, 1] >> 16) & 0xFF; uint maxCoreIdPerPackage; if (maxCpuid >= 4) maxCoreIdPerPackage = ((Data[4, 0] >> 26) & 0x3F) + 1; else maxCoreIdPerPackage = 1; threadMaskWith = NextLog2(maxCoreAndThreadIdPerPackage / maxCoreIdPerPackage); coreMaskWith = NextLog2(maxCoreIdPerPackage); break; case Vendor.AMD: uint corePerPackage; if (maxCpuidExt >= 8) corePerPackage = (ExtData[8, 2] & 0xFF) + 1; else corePerPackage = 1; threadMaskWith = 0; coreMaskWith = NextLog2(corePerPackage); if (Family == 0x17) { // ApicIdCoreIdSize: APIC ID size. // cores per DIE // we need this for Ryzen 5 (4 cores, 8 threads) ans Ryzen 6 (6 cores, 12 threads) // Ryzen 5: [core0][core1][dummy][dummy][core2][core3] (Core0 EBX = 00080800, Core2 EBX = 08080800) uint max_cores_per_die = (ExtData[8, 2] >> 12) & 0xF; switch (max_cores_per_die) { case 0x04: // Ryzen coreMaskWith = NextLog2(16); break; case 0x05: // Threadripper coreMaskWith = NextLog2(32); break; case 0x06: // Epic coreMaskWith = NextLog2(64); break; } } break; default: threadMaskWith = 0; coreMaskWith = 0; break; } ProcessorId = (ApicId >> (int) (coreMaskWith + threadMaskWith)); CoreId = ((ApicId >> (int) (threadMaskWith)) - (ProcessorId << (int) (coreMaskWith))); ThreadId = ApicId - (ProcessorId << (int) (coreMaskWith + threadMaskWith)) - (CoreId << (int) (threadMaskWith)); } public uint ApicId { get; } public string BrandString { get; } = ""; public uint CoreId { get; } public uint[,] Data { get; } = new uint[0, 0]; public uint[,] ExtData { get; } = new uint[0, 0]; public uint Family { get; } public uint Model { get; } public string Name { get; } = ""; public uint ProcessorId { get; } public uint Stepping { get; } public int Thread { get; } public uint ThreadId { get; } public Vendor Vendor { get; } = Vendor.Unknown; private static void AppendRegister(StringBuilder b, uint value) { b.Append((char) ((value) & 0xff)); b.Append((char) ((value >> 8) & 0xff)); b.Append((char) ((value >> 16) & 0xff)); b.Append((char) ((value >> 24) & 0xff)); } private static uint NextLog2(long x) { if (x <= 0) return 0; x--; uint count = 0; while (x > 0) { x >>= 1; count++; } return count; } } }