Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 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 | |
Dominik Brodowski | 2cd005c | 2011-04-19 20:16:05 +0200 | [diff] [blame] | 70 | if (!strncmp(value, "processor\t: ", 12)) |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 71 | sscanf(value, "processor\t: %u", &proc); |
Dominik Brodowski | 2cd005c | 2011-04-19 20:16:05 +0200 | [diff] [blame] | 72 | |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 73 | if (proc != cpu) |
| 74 | continue; |
| 75 | |
| 76 | /* Get CPU vendor */ |
Dominik Brodowski | 2cd005c | 2011-04-19 20:16:05 +0200 | [diff] [blame] | 77 | if (!strncmp(value, "vendor_id", 9)) { |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 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. */ |
Dominik Brodowski | 2cd005c | 2011-04-19 20:16:05 +0200 | [diff] [blame] | 83 | } else if (!strncmp(value, "cpu family\t: ", 13)) { |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 84 | sscanf(value, "cpu family\t: %u", |
| 85 | &cpu_info->family); |
Dominik Brodowski | 2cd005c | 2011-04-19 20:16:05 +0200 | [diff] [blame] | 86 | } else if (!strncmp(value, "model\t\t: ", 9)) { |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 87 | sscanf(value, "model\t\t: %u", |
| 88 | &cpu_info->model); |
Dominik Brodowski | 2cd005c | 2011-04-19 20:16:05 +0200 | [diff] [blame] | 89 | } else if (!strncmp(value, "stepping\t: ", 10)) { |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 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 | |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 133 | if (cpu_info->vendor == X86_VENDOR_INTEL) { |
Thomas Renninger | 029e9f7 | 2011-07-21 11:54:54 +0200 | [diff] [blame] | 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) { |
Thomas Renninger | 8fb2e44 | 2011-07-21 11:54:53 +0200 | [diff] [blame] | 140 | /* Intel's perf-bias MSR support */ |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 141 | if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3))) |
| 142 | cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS; |
Thomas Renninger | 8fb2e44 | 2011-07-21 11:54:53 +0200 | [diff] [blame] | 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 */ |
Thomas Renninger | 8d219e3 | 2012-11-27 13:17:49 +0100 | [diff] [blame] | 161 | case 0x3A: /* IVB */ |
| 162 | case 0x3E: /* IVB Xeon */ |
Thomas Renninger | 8fb2e44 | 2011-07-21 11:54:53 +0200 | [diff] [blame] | 163 | cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; |
| 164 | cpu_info->caps |= CPUPOWER_CAP_IS_SNB; |
| 165 | break; |
| 166 | case 0x2E: /* Nehalem-EX Xeon - Beckton */ |
| 167 | case 0x2F: /* Westmere-EX Xeon - Eagleton */ |
| 168 | default: |
| 169 | break; |
| 170 | } |
| 171 | } |
Dominik Brodowski | 7fe2f63 | 2011-03-30 16:30:11 +0200 | [diff] [blame] | 172 | } |
| 173 | |
| 174 | /* printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n", |
| 175 | cpuid_level, ext_cpuid_level, cpu_info->caps); |
| 176 | */ |
| 177 | return ret; |
| 178 | } |