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
9static 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	}
30cpuid_func(eax);
31cpuid_func(ebx);
32cpuid_func(ecx);
33cpuid_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 */
45int 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;
107out:
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