// 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) LibreHardwareMonitor and Contributors. // Partial Copyright (C) Michael Möller and Contributors. // All Rights Reserved. using System; using System.Text; namespace LibreHardwareMonitor.Hardware.Cpu; public enum Vendor { Unknown, Intel, AMD } public class CpuId { /// /// Initializes a new instance of the class. /// /// The group. /// The thread. /// The affinity. private CpuId(int group, int thread, GroupAffinity affinity) { Thread = thread; Group = group; Affinity = affinity; uint threadMaskWith; uint coreMaskWith; uint maxCpuidExt; if (thread >= 64) throw new ArgumentOutOfRangeException(nameof(thread)); uint maxCpuid; if (OpCode.CpuId(CPUID_0, 0, out uint eax, out uint ebx, out uint ecx, out uint edx)) { if (eax > 0) maxCpuid = eax; else return; StringBuilder vendorBuilder = new(); AppendRegister(vendorBuilder, ebx); AppendRegister(vendorBuilder, edx); AppendRegister(vendorBuilder, ecx); Vendor = vendorBuilder.ToString() switch { "GenuineIntel" => Vendor.Intel, "AuthenticAMD" => Vendor.AMD, _ => Vendor.Unknown }; if (OpCode.CpuId(CPUID_EXT, 0, out eax, out _, out _, out _)) { if (eax > CPUID_EXT) maxCpuidExt = eax - CPUID_EXT; else return; } else { throw new ArgumentOutOfRangeException(nameof(thread)); } } else { throw new ArgumentOutOfRangeException(nameof(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.CpuId(CPUID_0 + i, 0, out Data[i, 0], out Data[i, 1], out Data[i, 2], out Data[i, 3]); } ExtData = new uint[maxCpuidExt + 1, 4]; for (uint i = 0; i < maxCpuidExt + 1; i++) { OpCode.CpuId(CPUID_EXT + i, 0, out ExtData[i, 0], out ExtData[i, 1], out ExtData[i, 2], out ExtData[i, 3]); } StringBuilder nameBuilder = new(); for (uint i = 2; i <= 4; i++) { if (OpCode.CpuId(CPUID_EXT + i, 0, out eax, out ebx, out ecx, out edx)) { AppendRegister(nameBuilder, eax); AppendRegister(nameBuilder, ebx); AppendRegister(nameBuilder, ecx); AppendRegister(nameBuilder, edx); } } nameBuilder.Replace('\0', ' '); BrandString = nameBuilder.ToString().Trim(); nameBuilder.Replace("(R)", string.Empty); nameBuilder.Replace("(TM)", string.Empty); nameBuilder.Replace("(tm)", string.Empty); nameBuilder.Replace("CPU", string.Empty); nameBuilder.Replace("Dual-Core Processor", string.Empty); nameBuilder.Replace("Triple-Core Processor", string.Empty); nameBuilder.Replace("Quad-Core Processor", string.Empty); nameBuilder.Replace("Six-Core Processor", string.Empty); nameBuilder.Replace("Eight-Core Processor", string.Empty); nameBuilder.Replace("64-Core Processor", string.Empty); nameBuilder.Replace("32-Core Processor", string.Empty); nameBuilder.Replace("24-Core Processor", string.Empty); nameBuilder.Replace("16-Core Processor", string.Empty); nameBuilder.Replace("12-Core Processor", string.Empty); nameBuilder.Replace("8-Core Processor", string.Empty); nameBuilder.Replace("6-Core Processor", string.Empty); 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; PkgType = (ExtData[1, 1] >> 28) & 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 is 0x17 or 0x19) { // 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) coreMaskWith = ((ExtData[8, 2] >> 12) & 0xF) switch { 0x04 => NextLog2(16), // Ryzen 0x05 => NextLog2(32), // Threadripper 0x06 => NextLog2(64), // Epic _ => coreMaskWith }; } 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 GroupAffinity Affinity { get; } public uint ApicId { get; } public string BrandString { get; } = string.Empty; 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 int Group { get; } public uint Model { get; } public string Name { get; } = string.Empty; public uint PkgType { get; } public uint ProcessorId { get; } public uint Stepping { get; } public int Thread { get; } public uint ThreadId { get; } public Vendor Vendor { get; } = Vendor.Unknown; /// /// Gets the specified . /// /// The group. /// The thread. /// . public static CpuId Get(int group, int thread) { if (thread >= 64) return null; var affinity = GroupAffinity.Single((ushort)group, thread); GroupAffinity previousAffinity = ThreadAffinity.Set(affinity); if (previousAffinity == GroupAffinity.Undefined) return null; try { return new CpuId(group, thread, affinity); } finally { ThreadAffinity.Set(previousAffinity); } } 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; } // ReSharper disable InconsistentNaming public const uint CPUID_0 = 0; public const uint CPUID_EXT = 0x80000000; // ReSharper restore InconsistentNaming }