1 #include <stdio.h> 2 #include <errno.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <stdlib.h> 6 7 #include "helpers/helpers.h" 8 9 static const char *cpu_vendor_table[X86_VENDOR_MAX] = { 10 "Unknown", "GenuineIntel", "AuthenticAMD", 11 }; 12 13 #if defined(__i386__) || defined(__x86_64__) 14 15 /* from gcc */ 16 #include <cpuid.h> 17 18 /* 19 * CPUID functions returning a single datum 20 * 21 * Define unsigned int cpuid_e[abcd]x(unsigned int op) 22 */ 23 #define cpuid_func(reg) \ 24 unsigned int cpuid_##reg(unsigned int op) \ 25 { \ 26 unsigned int eax, ebx, ecx, edx; \ 27 __cpuid(op, eax, ebx, ecx, edx); \ 28 return reg; \ 29 } 30 cpuid_func(eax); 31 cpuid_func(ebx); 32 cpuid_func(ecx); 33 cpuid_func(edx); 34 35 #endif /* defined(__i386__) || defined(__x86_64__) */ 36 37 /* get_cpu_info 38 * 39 * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo 40 * 41 * Returns 0 on success or a negativ error code 42 * 43 * TBD: Should there be a cpuid alternative for this if /proc is not mounted? 44 */ 45 int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info) 46 { 47 FILE *fp; 48 char value[64]; 49 unsigned int proc, x; 50 unsigned int unknown = 0xffffff; 51 unsigned int cpuid_level, ext_cpuid_level; 52 53 int ret = -EINVAL; 54 55 cpu_info->vendor = X86_VENDOR_UNKNOWN; 56 cpu_info->family = unknown; 57 cpu_info->model = unknown; 58 cpu_info->stepping = unknown; 59 cpu_info->caps = 0; 60 61 fp = fopen("/proc/cpuinfo", "r"); 62 if (!fp) 63 return -EIO; 64 65 while (!feof(fp)) { 66 if (!fgets(value, 64, fp)) 67 continue; 68 value[63 - 1] = '\0'; 69 70 if (!strncmp(value, "processor\t: ", 12)) 71 sscanf(value, "processor\t: %u", &proc); 72 73 if (proc != cpu) 74 continue; 75 76 /* Get CPU vendor */ 77 if (!strncmp(value, "vendor_id", 9)) { 78 for (x = 1; x < X86_VENDOR_MAX; x++) { 79 if (strstr(value, cpu_vendor_table[x])) 80 cpu_info->vendor = x; 81 } 82 /* Get CPU family, etc. */ 83 } else if (!strncmp(value, "cpu family\t: ", 13)) { 84 sscanf(value, "cpu family\t: %u", 85 &cpu_info->family); 86 } else if (!strncmp(value, "model\t\t: ", 9)) { 87 sscanf(value, "model\t\t: %u", 88 &cpu_info->model); 89 } else if (!strncmp(value, "stepping\t: ", 10)) { 90 sscanf(value, "stepping\t: %u", 91 &cpu_info->stepping); 92 93 /* Exit -> all values must have been set */ 94 if (cpu_info->vendor == X86_VENDOR_UNKNOWN || 95 cpu_info->family == unknown || 96 cpu_info->model == unknown || 97 cpu_info->stepping == unknown) { 98 ret = -EINVAL; 99 goto out; 100 } 101 102 ret = 0; 103 goto out; 104 } 105 } 106 ret = -ENODEV; 107 out: 108 fclose(fp); 109 /* Get some useful CPU capabilities from cpuid */ 110 if (cpu_info->vendor != X86_VENDOR_AMD && 111 cpu_info->vendor != X86_VENDOR_INTEL) 112 return ret; 113 114 cpuid_level = cpuid_eax(0); 115 ext_cpuid_level = cpuid_eax(0x80000000); 116 117 /* Invariant TSC */ 118 if (ext_cpuid_level >= 0x80000007 && 119 (cpuid_edx(0x80000007) & (1 << 8))) 120 cpu_info->caps |= CPUPOWER_CAP_INV_TSC; 121 122 /* Aperf/Mperf registers support */ 123 if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1)) 124 cpu_info->caps |= CPUPOWER_CAP_APERF; 125 126 /* AMD Boost state enable/disable register */ 127 if (cpu_info->vendor == X86_VENDOR_AMD) { 128 if (ext_cpuid_level >= 0x80000007 && 129 (cpuid_edx(0x80000007) & (1 << 9))) 130 cpu_info->caps |= CPUPOWER_CAP_AMD_CBP; 131 } 132 133 if (cpu_info->vendor == X86_VENDOR_INTEL) { 134 if (cpuid_level >= 6 && 135 (cpuid_eax(6) & (1 << 1))) 136 cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA; 137 } 138 139 if (cpu_info->vendor == X86_VENDOR_INTEL) { 140 /* Intel's perf-bias MSR support */ 141 if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) 142 cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; 143 144 /* Intel's Turbo Ratio Limit support */ 145 if (cpu_info->family == 6) { 146 switch (cpu_info->model) { 147 case 0x1A: /* Core i7, Xeon 5500 series 148 * Bloomfield, Gainstown NHM-EP 149 */ 150 case 0x1E: /* Core i7 and i5 Processor 151 * Clarksfield, Lynnfield, Jasper Forest 152 */ 153 case 0x1F: /* Core i7 and i5 Processor - Nehalem */ 154 case 0x25: /* Westmere Client 155 * Clarkdale, Arrandale 156 */ 157 case 0x2C: /* Westmere EP - Gulftown */ 158 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 159 case 0x2A: /* SNB */ 160 case 0x2D: /* SNB Xeon */ 161 cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; 162 cpu_info->caps |= CPUPOWER_CAP_IS_SNB; 163 break; 164 case 0x2E: /* Nehalem-EX Xeon - Beckton */ 165 case 0x2F: /* Westmere-EX Xeon - Eagleton */ 166 default: 167 break; 168 } 169 } 170 } 171 172 /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", 173 cpuid_level, ext_cpuid_level, cpu_info->caps); 174 */ 175 return ret; 176 } 177