Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cpuid.c
Go to the documentation of this file.
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 }