xref: /OK3568_Linux_fs/kernel/tools/power/cpupower/utils/helpers/cpuid.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <stdio.h>
3*4882a593Smuzhiyun #include <errno.h>
4*4882a593Smuzhiyun #include <string.h>
5*4882a593Smuzhiyun #include <unistd.h>
6*4882a593Smuzhiyun #include <stdlib.h>
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include "helpers/helpers.h"
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
11*4882a593Smuzhiyun 	"Unknown", "GenuineIntel", "AuthenticAMD", "HygonGenuine",
12*4882a593Smuzhiyun };
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #if defined(__i386__) || defined(__x86_64__)
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun /* from gcc */
17*4882a593Smuzhiyun #include <cpuid.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun /*
20*4882a593Smuzhiyun  * CPUID functions returning a single datum
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * Define unsigned int cpuid_e[abcd]x(unsigned int op)
23*4882a593Smuzhiyun  */
24*4882a593Smuzhiyun #define cpuid_func(reg)					\
25*4882a593Smuzhiyun 	unsigned int cpuid_##reg(unsigned int op)	\
26*4882a593Smuzhiyun 	{						\
27*4882a593Smuzhiyun 	unsigned int eax, ebx, ecx, edx;		\
28*4882a593Smuzhiyun 	__cpuid(op, eax, ebx, ecx, edx);		\
29*4882a593Smuzhiyun 	return reg;					\
30*4882a593Smuzhiyun 	}
31*4882a593Smuzhiyun cpuid_func(eax);
32*4882a593Smuzhiyun cpuid_func(ebx);
33*4882a593Smuzhiyun cpuid_func(ecx);
34*4882a593Smuzhiyun cpuid_func(edx);
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #endif /* defined(__i386__) || defined(__x86_64__) */
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun /* get_cpu_info
39*4882a593Smuzhiyun  *
40*4882a593Smuzhiyun  * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
41*4882a593Smuzhiyun  *
42*4882a593Smuzhiyun  * Returns 0 on success or a negativ error code
43*4882a593Smuzhiyun  *
44*4882a593Smuzhiyun  * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
45*4882a593Smuzhiyun  */
get_cpu_info(struct cpupower_cpu_info * cpu_info)46*4882a593Smuzhiyun int get_cpu_info(struct cpupower_cpu_info *cpu_info)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	FILE *fp;
49*4882a593Smuzhiyun 	char value[64];
50*4882a593Smuzhiyun 	unsigned int proc, x;
51*4882a593Smuzhiyun 	unsigned int unknown = 0xffffff;
52*4882a593Smuzhiyun 	unsigned int cpuid_level, ext_cpuid_level;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	int ret = -EINVAL;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	cpu_info->vendor		= X86_VENDOR_UNKNOWN;
57*4882a593Smuzhiyun 	cpu_info->family		= unknown;
58*4882a593Smuzhiyun 	cpu_info->model			= unknown;
59*4882a593Smuzhiyun 	cpu_info->stepping		= unknown;
60*4882a593Smuzhiyun 	cpu_info->caps			= 0;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	fp = fopen("/proc/cpuinfo", "r");
63*4882a593Smuzhiyun 	if (!fp)
64*4882a593Smuzhiyun 		return -EIO;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	while (!feof(fp)) {
67*4882a593Smuzhiyun 		if (!fgets(value, 64, fp))
68*4882a593Smuzhiyun 			continue;
69*4882a593Smuzhiyun 		value[63 - 1] = '\0';
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 		if (!strncmp(value, "processor\t: ", 12))
72*4882a593Smuzhiyun 			sscanf(value, "processor\t: %u", &proc);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 		if (proc != (unsigned int)base_cpu)
75*4882a593Smuzhiyun 			continue;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 		/* Get CPU vendor */
78*4882a593Smuzhiyun 		if (!strncmp(value, "vendor_id", 9)) {
79*4882a593Smuzhiyun 			for (x = 1; x < X86_VENDOR_MAX; x++) {
80*4882a593Smuzhiyun 				if (strstr(value, cpu_vendor_table[x]))
81*4882a593Smuzhiyun 					cpu_info->vendor = x;
82*4882a593Smuzhiyun 			}
83*4882a593Smuzhiyun 		/* Get CPU family, etc. */
84*4882a593Smuzhiyun 		} else if (!strncmp(value, "cpu family\t: ", 13)) {
85*4882a593Smuzhiyun 			sscanf(value, "cpu family\t: %u",
86*4882a593Smuzhiyun 			       &cpu_info->family);
87*4882a593Smuzhiyun 		} else if (!strncmp(value, "model\t\t: ", 9)) {
88*4882a593Smuzhiyun 			sscanf(value, "model\t\t: %u",
89*4882a593Smuzhiyun 			       &cpu_info->model);
90*4882a593Smuzhiyun 		} else if (!strncmp(value, "stepping\t: ", 10)) {
91*4882a593Smuzhiyun 			sscanf(value, "stepping\t: %u",
92*4882a593Smuzhiyun 			       &cpu_info->stepping);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 			/* Exit -> all values must have been set */
95*4882a593Smuzhiyun 			if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
96*4882a593Smuzhiyun 			    cpu_info->family == unknown ||
97*4882a593Smuzhiyun 			    cpu_info->model == unknown ||
98*4882a593Smuzhiyun 			    cpu_info->stepping == unknown) {
99*4882a593Smuzhiyun 				ret = -EINVAL;
100*4882a593Smuzhiyun 				goto out;
101*4882a593Smuzhiyun 			}
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 			ret = 0;
104*4882a593Smuzhiyun 			goto out;
105*4882a593Smuzhiyun 		}
106*4882a593Smuzhiyun 	}
107*4882a593Smuzhiyun 	ret = -ENODEV;
108*4882a593Smuzhiyun out:
109*4882a593Smuzhiyun 	fclose(fp);
110*4882a593Smuzhiyun 	/* Get some useful CPU capabilities from cpuid */
111*4882a593Smuzhiyun 	if (cpu_info->vendor != X86_VENDOR_AMD &&
112*4882a593Smuzhiyun 	    cpu_info->vendor != X86_VENDOR_HYGON &&
113*4882a593Smuzhiyun 	    cpu_info->vendor != X86_VENDOR_INTEL)
114*4882a593Smuzhiyun 		return ret;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	cpuid_level	= cpuid_eax(0);
117*4882a593Smuzhiyun 	ext_cpuid_level	= cpuid_eax(0x80000000);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	/* Invariant TSC */
120*4882a593Smuzhiyun 	if (ext_cpuid_level >= 0x80000007 &&
121*4882a593Smuzhiyun 	    (cpuid_edx(0x80000007) & (1 << 8)))
122*4882a593Smuzhiyun 		cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	/* Aperf/Mperf registers support */
125*4882a593Smuzhiyun 	if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
126*4882a593Smuzhiyun 		cpu_info->caps |= CPUPOWER_CAP_APERF;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	/* AMD or Hygon Boost state enable/disable register */
129*4882a593Smuzhiyun 	if (cpu_info->vendor == X86_VENDOR_AMD ||
130*4882a593Smuzhiyun 	    cpu_info->vendor == X86_VENDOR_HYGON) {
131*4882a593Smuzhiyun 		if (ext_cpuid_level >= 0x80000007 &&
132*4882a593Smuzhiyun 		    (cpuid_edx(0x80000007) & (1 << 9)))
133*4882a593Smuzhiyun 			cpu_info->caps |= CPUPOWER_CAP_AMD_CBP;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 		if (ext_cpuid_level >= 0x80000008 &&
136*4882a593Smuzhiyun 		    cpuid_ebx(0x80000008) & (1 << 4))
137*4882a593Smuzhiyun 			cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU;
138*4882a593Smuzhiyun 	}
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
141*4882a593Smuzhiyun 		if (cpuid_level >= 6 &&
142*4882a593Smuzhiyun 		    (cpuid_eax(6) & (1 << 1)))
143*4882a593Smuzhiyun 			cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
144*4882a593Smuzhiyun 	}
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
147*4882a593Smuzhiyun 		/* Intel's perf-bias MSR support */
148*4882a593Smuzhiyun 		if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
149*4882a593Smuzhiyun 			cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 		/* Intel's Turbo Ratio Limit support */
152*4882a593Smuzhiyun 		if (cpu_info->family == 6) {
153*4882a593Smuzhiyun 			switch (cpu_info->model) {
154*4882a593Smuzhiyun 			case 0x1A:	/* Core i7, Xeon 5500 series
155*4882a593Smuzhiyun 					 * Bloomfield, Gainstown NHM-EP
156*4882a593Smuzhiyun 					 */
157*4882a593Smuzhiyun 			case 0x1E:	/* Core i7 and i5 Processor
158*4882a593Smuzhiyun 					 * Clarksfield, Lynnfield, Jasper Forest
159*4882a593Smuzhiyun 					 */
160*4882a593Smuzhiyun 			case 0x1F:	/* Core i7 and i5 Processor - Nehalem */
161*4882a593Smuzhiyun 			case 0x25:	/* Westmere Client
162*4882a593Smuzhiyun 					 * Clarkdale, Arrandale
163*4882a593Smuzhiyun 					 */
164*4882a593Smuzhiyun 			case 0x2C:	/* Westmere EP - Gulftown */
165*4882a593Smuzhiyun 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
166*4882a593Smuzhiyun 				break;
167*4882a593Smuzhiyun 			case 0x2A:	/* SNB */
168*4882a593Smuzhiyun 			case 0x2D:	/* SNB Xeon */
169*4882a593Smuzhiyun 			case 0x3A:	/* IVB */
170*4882a593Smuzhiyun 			case 0x3E:	/* IVB Xeon */
171*4882a593Smuzhiyun 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
172*4882a593Smuzhiyun 				cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
173*4882a593Smuzhiyun 				break;
174*4882a593Smuzhiyun 			case 0x2E:	/* Nehalem-EX Xeon - Beckton */
175*4882a593Smuzhiyun 			case 0x2F:	/* Westmere-EX Xeon - Eagleton */
176*4882a593Smuzhiyun 			default:
177*4882a593Smuzhiyun 				break;
178*4882a593Smuzhiyun 			}
179*4882a593Smuzhiyun 		}
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	/*	printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
183*4882a593Smuzhiyun 		cpuid_level, ext_cpuid_level, cpu_info->caps);
184*4882a593Smuzhiyun 	*/
185*4882a593Smuzhiyun 	return ret;
186*4882a593Smuzhiyun }
187