xref: /linux/tools/power/cpupower/utils/helpers/cpuid.c (revision 7fe2f6399a84760a9af8896ac152728250f82adb)
1*7fe2f639SDominik Brodowski #include <stdio.h>
2*7fe2f639SDominik Brodowski #include <errno.h>
3*7fe2f639SDominik Brodowski #include <string.h>
4*7fe2f639SDominik Brodowski #include <unistd.h>
5*7fe2f639SDominik Brodowski #include <stdlib.h>
6*7fe2f639SDominik Brodowski 
7*7fe2f639SDominik Brodowski #include "helpers/helpers.h"
8*7fe2f639SDominik Brodowski 
9*7fe2f639SDominik Brodowski static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
10*7fe2f639SDominik Brodowski 	"Unknown", "GenuineIntel", "AuthenticAMD",
11*7fe2f639SDominik Brodowski };
12*7fe2f639SDominik Brodowski 
13*7fe2f639SDominik Brodowski #if defined(__i386__) || defined(__x86_64__)
14*7fe2f639SDominik Brodowski 
15*7fe2f639SDominik Brodowski /* from gcc */
16*7fe2f639SDominik Brodowski #include <cpuid.h>
17*7fe2f639SDominik Brodowski 
18*7fe2f639SDominik Brodowski /*
19*7fe2f639SDominik Brodowski  * CPUID functions returning a single datum
20*7fe2f639SDominik Brodowski  *
21*7fe2f639SDominik Brodowski  * Define unsigned int cpuid_e[abcd]x(unsigned int op)
22*7fe2f639SDominik Brodowski  */
23*7fe2f639SDominik Brodowski #define cpuid_func(reg)					\
24*7fe2f639SDominik Brodowski 	unsigned int cpuid_##reg(unsigned int op)	\
25*7fe2f639SDominik Brodowski 	{						\
26*7fe2f639SDominik Brodowski 	unsigned int eax, ebx, ecx, edx;		\
27*7fe2f639SDominik Brodowski 	__cpuid(op, eax, ebx, ecx, edx);		\
28*7fe2f639SDominik Brodowski 	return reg;					\
29*7fe2f639SDominik Brodowski 	}
30*7fe2f639SDominik Brodowski cpuid_func(eax);
31*7fe2f639SDominik Brodowski cpuid_func(ebx);
32*7fe2f639SDominik Brodowski cpuid_func(ecx);
33*7fe2f639SDominik Brodowski cpuid_func(edx);
34*7fe2f639SDominik Brodowski 
35*7fe2f639SDominik Brodowski #endif /* defined(__i386__) || defined(__x86_64__) */
36*7fe2f639SDominik Brodowski 
37*7fe2f639SDominik Brodowski /* get_cpu_info
38*7fe2f639SDominik Brodowski  *
39*7fe2f639SDominik Brodowski  * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
40*7fe2f639SDominik Brodowski  *
41*7fe2f639SDominik Brodowski  * Returns 0 on success or a negativ error code
42*7fe2f639SDominik Brodowski  *
43*7fe2f639SDominik Brodowski  * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
44*7fe2f639SDominik Brodowski  */
45*7fe2f639SDominik Brodowski int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info)
46*7fe2f639SDominik Brodowski {
47*7fe2f639SDominik Brodowski 	FILE *fp;
48*7fe2f639SDominik Brodowski 	char value[64];
49*7fe2f639SDominik Brodowski 	unsigned int proc, x;
50*7fe2f639SDominik Brodowski 	unsigned int unknown = 0xffffff;
51*7fe2f639SDominik Brodowski 	unsigned int cpuid_level, ext_cpuid_level;
52*7fe2f639SDominik Brodowski 
53*7fe2f639SDominik Brodowski 	int ret = -EINVAL;
54*7fe2f639SDominik Brodowski 
55*7fe2f639SDominik Brodowski 	cpu_info->vendor		= X86_VENDOR_UNKNOWN;
56*7fe2f639SDominik Brodowski 	cpu_info->family		= unknown;
57*7fe2f639SDominik Brodowski 	cpu_info->model			= unknown;
58*7fe2f639SDominik Brodowski 	cpu_info->stepping		= unknown;
59*7fe2f639SDominik Brodowski 	cpu_info->caps			= 0;
60*7fe2f639SDominik Brodowski 
61*7fe2f639SDominik Brodowski 	fp = fopen("/proc/cpuinfo", "r");
62*7fe2f639SDominik Brodowski 	if (!fp)
63*7fe2f639SDominik Brodowski 		return -EIO;
64*7fe2f639SDominik Brodowski 
65*7fe2f639SDominik Brodowski 	while (!feof(fp)) {
66*7fe2f639SDominik Brodowski 		if (!fgets(value, 64, fp))
67*7fe2f639SDominik Brodowski 			continue;
68*7fe2f639SDominik Brodowski 		value[63 - 1] = '\0';
69*7fe2f639SDominik Brodowski 
70*7fe2f639SDominik Brodowski 		if (!strncmp(value, "processor\t: ", 12)) {
71*7fe2f639SDominik Brodowski 			sscanf(value, "processor\t: %u", &proc);
72*7fe2f639SDominik Brodowski 		}
73*7fe2f639SDominik Brodowski 		if (proc != cpu)
74*7fe2f639SDominik Brodowski 			continue;
75*7fe2f639SDominik Brodowski 
76*7fe2f639SDominik Brodowski 		/* Get CPU vendor */
77*7fe2f639SDominik Brodowski 		if (!strncmp(value, "vendor_id", 9))
78*7fe2f639SDominik Brodowski 			for (x = 1; x < X86_VENDOR_MAX; x++) {
79*7fe2f639SDominik Brodowski 				if (strstr(value, cpu_vendor_table[x]))
80*7fe2f639SDominik Brodowski 					cpu_info->vendor = x;
81*7fe2f639SDominik Brodowski 			}
82*7fe2f639SDominik Brodowski 		/* Get CPU family, etc. */
83*7fe2f639SDominik Brodowski 		else if (!strncmp(value, "cpu family\t: ", 13)) {
84*7fe2f639SDominik Brodowski 			sscanf(value, "cpu family\t: %u",
85*7fe2f639SDominik Brodowski 			       &cpu_info->family);
86*7fe2f639SDominik Brodowski 		}
87*7fe2f639SDominik Brodowski 		else if (!strncmp(value, "model\t\t: ", 9)) {
88*7fe2f639SDominik Brodowski 			sscanf(value, "model\t\t: %u",
89*7fe2f639SDominik Brodowski 			       &cpu_info->model);
90*7fe2f639SDominik Brodowski 		}
91*7fe2f639SDominik Brodowski 		else if (!strncmp(value, "stepping\t: ", 10)) {
92*7fe2f639SDominik Brodowski 			sscanf(value, "stepping\t: %u",
93*7fe2f639SDominik Brodowski 			       &cpu_info->stepping);
94*7fe2f639SDominik Brodowski 
95*7fe2f639SDominik Brodowski 			/* Exit -> all values must have been set */
96*7fe2f639SDominik Brodowski 			if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
97*7fe2f639SDominik Brodowski 			    cpu_info->family == unknown ||
98*7fe2f639SDominik Brodowski 			    cpu_info->model == unknown ||
99*7fe2f639SDominik Brodowski 			    cpu_info->stepping == unknown) {
100*7fe2f639SDominik Brodowski 				ret = -EINVAL;
101*7fe2f639SDominik Brodowski 				goto out;
102*7fe2f639SDominik Brodowski 			}
103*7fe2f639SDominik Brodowski 
104*7fe2f639SDominik Brodowski 			ret = 0;
105*7fe2f639SDominik Brodowski 			goto out;
106*7fe2f639SDominik Brodowski 		}
107*7fe2f639SDominik Brodowski 	}
108*7fe2f639SDominik Brodowski 	ret = -ENODEV;
109*7fe2f639SDominik Brodowski out:
110*7fe2f639SDominik Brodowski 	fclose(fp);
111*7fe2f639SDominik Brodowski 	/* Get some useful CPU capabilities from cpuid */
112*7fe2f639SDominik Brodowski 	if (cpu_info->vendor != X86_VENDOR_AMD &&
113*7fe2f639SDominik Brodowski 	    cpu_info->vendor != X86_VENDOR_INTEL)
114*7fe2f639SDominik Brodowski 		return ret;
115*7fe2f639SDominik Brodowski 
116*7fe2f639SDominik Brodowski 	cpuid_level	= cpuid_eax(0);
117*7fe2f639SDominik Brodowski 	ext_cpuid_level	= cpuid_eax(0x80000000);
118*7fe2f639SDominik Brodowski 
119*7fe2f639SDominik Brodowski 	/* Invariant TSC */
120*7fe2f639SDominik Brodowski 	if (ext_cpuid_level >= 0x80000007 &&
121*7fe2f639SDominik Brodowski 	    (cpuid_edx(0x80000007) & (1 << 8)))
122*7fe2f639SDominik Brodowski 		cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
123*7fe2f639SDominik Brodowski 
124*7fe2f639SDominik Brodowski 	/* Aperf/Mperf registers support */
125*7fe2f639SDominik Brodowski 	if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
126*7fe2f639SDominik Brodowski 		cpu_info->caps |= CPUPOWER_CAP_APERF;
127*7fe2f639SDominik Brodowski 
128*7fe2f639SDominik Brodowski 	/* AMD Boost state enable/disable register */
129*7fe2f639SDominik Brodowski 	if (cpu_info->vendor == X86_VENDOR_AMD) {
130*7fe2f639SDominik Brodowski 		if (ext_cpuid_level >= 0x80000007 &&
131*7fe2f639SDominik Brodowski 		    (cpuid_edx(0x80000007) & (1 << 9)))
132*7fe2f639SDominik Brodowski 			cpu_info->caps |= CPUPOWER_CAP_AMD_CBP;
133*7fe2f639SDominik Brodowski 	}
134*7fe2f639SDominik Brodowski 
135*7fe2f639SDominik Brodowski 	/* Intel's perf-bias MSR support */
136*7fe2f639SDominik Brodowski 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
137*7fe2f639SDominik Brodowski 		if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
138*7fe2f639SDominik Brodowski 			cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
139*7fe2f639SDominik Brodowski 	}
140*7fe2f639SDominik Brodowski 
141*7fe2f639SDominik Brodowski 	/*	printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
142*7fe2f639SDominik Brodowski 		cpuid_level, ext_cpuid_level, cpu_info->caps);
143*7fe2f639SDominik Brodowski 	*/
144*7fe2f639SDominik Brodowski 	return ret;
145*7fe2f639SDominik Brodowski }
146