|
| 1 | +//go:build amd64 |
| 2 | + |
| 3 | +package x86 |
| 4 | + |
| 5 | +const ( |
| 6 | + // CPUID_TIME_STAMP_COUNTER |
| 7 | + // EAX Returns processor base frequency information described by the |
| 8 | + // type CPUID_PROCESSOR_FREQUENCY_EAX. |
| 9 | + // EBX Returns maximum frequency information described by the type |
| 10 | + // CPUID_PROCESSOR_FREQUENCY_EBX. |
| 11 | + // ECX Returns bus frequency information described by the type |
| 12 | + // CPUID_PROCESSOR_FREQUENCY_ECX. |
| 13 | + // EDX Reserved. |
| 14 | + CPUID_TIME_STAMP_COUNTER = 0x15 |
| 15 | + |
| 16 | + // CPUID_PROCESSOR_FREQUENCY |
| 17 | + // EAX Returns processor base frequency information described by the |
| 18 | + // type CPUID_PROCESSOR_FREQUENCY_EAX. |
| 19 | + // EBX Returns maximum frequency information described by the type |
| 20 | + // CPUID_PROCESSOR_FREQUENCY_EBX. |
| 21 | + // ECX Returns bus frequency information described by the type |
| 22 | + // CPUID_PROCESSOR_FREQUENCY_ECX. |
| 23 | + // EDX Reserved. |
| 24 | + CPUID_PROCESSOR_FREQUENCY = 0x16 |
| 25 | +) |
| 26 | + |
| 27 | +type CpuExtendedFamily uint16 |
| 28 | + |
| 29 | +const ( |
| 30 | + // AMD |
| 31 | + CPU_FAMILY_AMD_11H CpuExtendedFamily = 0x11 |
| 32 | + // Intel |
| 33 | + CPU_FAMILY_INTEL_CORE CpuExtendedFamily = 6 |
| 34 | + CPU_MODEL_NEHALEM CpuExtendedFamily = 0x1e |
| 35 | + CPU_MODEL_NEHALEM_EP CpuExtendedFamily = 0x1a |
| 36 | + CPU_MODEL_NEHALEM_EX CpuExtendedFamily = 0x2e |
| 37 | + CPU_MODEL_WESTMERE CpuExtendedFamily = 0x25 |
| 38 | + CPU_MODEL_WESTMERE_EP CpuExtendedFamily = 0x2c |
| 39 | + CPU_MODEL_WESTMERE_EX CpuExtendedFamily = 0x2f |
| 40 | + CPU_MODEL_SANDYBRIDGE CpuExtendedFamily = 0x2a |
| 41 | + CPU_MODEL_SANDYBRIDGE_EP CpuExtendedFamily = 0x2d |
| 42 | + CPU_MODEL_IVYBRIDGE_EP CpuExtendedFamily = 0x3a |
| 43 | + CPU_MODEL_HASWELL_E3 CpuExtendedFamily = 0x3c |
| 44 | + CPU_MODEL_HASWELL_E7 CpuExtendedFamily = 0x3f |
| 45 | + CPU_MODEL_BROADWELL CpuExtendedFamily = 0x3d |
| 46 | +) |
| 47 | + |
| 48 | +func GetExtendedCpuFamily() CpuExtendedFamily { |
| 49 | + var family CpuExtendedFamily |
| 50 | + family = CpuExtendedFamily((stdCpuid1Eax >> 8) & 0x0f) |
| 51 | + family += CpuExtendedFamily((stdCpuid1Eax >> 20) & 0xff) |
| 52 | + return family |
| 53 | +} |
| 54 | + |
| 55 | +//export asmPause |
| 56 | +func AsmPause() |
| 57 | + |
| 58 | +//export asmReadRdtsc |
| 59 | +func AsmReadRdtsc() uint64 |
| 60 | + |
| 61 | +//export asmCpuid |
| 62 | +func AsmCpuid(index uint32, registerEax *uint32, registerEbx *uint32, registerEcx *uint32, registerEdx *uint32) int |
| 63 | + |
| 64 | +var maxCpuidIndex uint32 |
| 65 | +var stdVendorName0 uint32 |
| 66 | +var stdCpuid1Eax uint32 |
| 67 | +var stdCpuid1Ebx uint32 |
| 68 | +var stdCpuid1Ecx uint32 |
| 69 | +var stdCpuid1Edx uint32 |
| 70 | + |
| 71 | +func init() { |
| 72 | + AsmCpuid(0, &maxCpuidIndex, &stdVendorName0, nil, nil) |
| 73 | + AsmCpuid(1, &stdCpuid1Eax, &stdCpuid1Ebx, &stdCpuid1Ecx, &stdCpuid1Edx) |
| 74 | +} |
| 75 | + |
| 76 | +func GetMaxCpuidIndex() uint32 { |
| 77 | + return maxCpuidIndex |
| 78 | +} |
| 79 | + |
| 80 | +func IsIntel() bool { |
| 81 | + return stdVendorName0 == 0x756e6547 |
| 82 | +} |
| 83 | + |
| 84 | +func IsIntelFamilyCore() bool { |
| 85 | + return IsIntel() && GetExtendedCpuFamily() == CPU_FAMILY_INTEL_CORE |
| 86 | +} |
| 87 | + |
| 88 | +func IsAmd() bool { |
| 89 | + return stdVendorName0 == 0x68747541 |
| 90 | +} |
| 91 | + |
| 92 | +func InternalGetPerformanceCounterFrequency() uint64 { |
| 93 | + if maxCpuidIndex >= CPUID_TIME_STAMP_COUNTER { |
| 94 | + return CpuidCoreClockCalculateTscFrequency() |
| 95 | + } |
| 96 | + |
| 97 | + return 0 |
| 98 | +} |
| 99 | + |
| 100 | +func CpuidCoreClockCalculateTscFrequency() uint64 { |
| 101 | + var TscFrequency uint64 |
| 102 | + var CoreXtalFrequency uint64 |
| 103 | + var RegEax uint32 |
| 104 | + var RegEbx uint32 |
| 105 | + var RegEcx uint32 |
| 106 | + |
| 107 | + AsmCpuid(CPUID_TIME_STAMP_COUNTER, &RegEax, &RegEbx, &RegEcx, nil) |
| 108 | + |
| 109 | + // If EAX or EBX returns 0, the XTAL ratio is not enumerated. |
| 110 | + if (RegEax == 0) || (RegEbx == 0) { |
| 111 | + return 0 |
| 112 | + } |
| 113 | + |
| 114 | + // If ECX returns 0, the XTAL frequency is not enumerated. |
| 115 | + // And PcdCpuCoreCrystalClockFrequency defined should base on processor series. |
| 116 | + // |
| 117 | + if RegEcx == 0 { |
| 118 | + // Specifies CPUID Leaf 0x15 Time Stamp Counter and Nominal Core Crystal Clock Frequency. |
| 119 | + // https://github.com/torvalds/linux/blob/master/tools/power/x86/turbostat/turbostat.c |
| 120 | + if IsIntelFamilyCore() { |
| 121 | + switch GetExtendedCpuFamily() { |
| 122 | + case 0x5F: // INTEL_FAM6_ATOM_GOLDMONT_D |
| 123 | + CoreXtalFrequency = 25000000 |
| 124 | + case 0x5C: // INTEL_FAM6_ATOM_GOLDMONT |
| 125 | + CoreXtalFrequency = 19200000 |
| 126 | + case 0x7A: // INTEL_FAM6_ATOM_GOLDMONT_PLUS |
| 127 | + CoreXtalFrequency = 19200000 |
| 128 | + default: |
| 129 | + CoreXtalFrequency = 24000000 |
| 130 | + } |
| 131 | + } else { |
| 132 | + return 0 |
| 133 | + } |
| 134 | + } else { |
| 135 | + CoreXtalFrequency = uint64(RegEcx) |
| 136 | + } |
| 137 | + |
| 138 | + // Calculate TSC frequency = (ECX, Core Xtal Frequency) * EBX/EAX |
| 139 | + TscFrequency = ((CoreXtalFrequency * uint64(RegEbx)) + (uint64(RegEax) / 2)) / uint64(RegEax) |
| 140 | + return TscFrequency |
| 141 | +} |
0 commit comments