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 			case 0x3A:	/* IVB */
162 			case 0x3E:	/* IVB Xeon */
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 		}
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 }
179