xref: /linux/tools/power/cpupower/utils/helpers/cpuid.c (revision 23765b82a808da416b70b41d711468e723531e6a)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
27fe2f639SDominik Brodowski #include <stdio.h>
37fe2f639SDominik Brodowski #include <errno.h>
47fe2f639SDominik Brodowski #include <string.h>
57fe2f639SDominik Brodowski #include <unistd.h>
67fe2f639SDominik Brodowski #include <stdlib.h>
77fe2f639SDominik Brodowski 
87fe2f639SDominik Brodowski #include "helpers/helpers.h"
97fe2f639SDominik Brodowski 
107fe2f639SDominik Brodowski static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
11995d5f64SPu Wen 	"Unknown", "GenuineIntel", "AuthenticAMD", "HygonGenuine",
127fe2f639SDominik Brodowski };
137fe2f639SDominik Brodowski 
147fe2f639SDominik Brodowski #if defined(__i386__) || defined(__x86_64__)
157fe2f639SDominik Brodowski 
167fe2f639SDominik Brodowski /* from gcc */
177fe2f639SDominik Brodowski #include <cpuid.h>
187fe2f639SDominik Brodowski 
197fe2f639SDominik Brodowski /*
207fe2f639SDominik Brodowski  * CPUID functions returning a single datum
217fe2f639SDominik Brodowski  *
227fe2f639SDominik Brodowski  * Define unsigned int cpuid_e[abcd]x(unsigned int op)
237fe2f639SDominik Brodowski  */
247fe2f639SDominik Brodowski #define cpuid_func(reg)					\
257fe2f639SDominik Brodowski 	unsigned int cpuid_##reg(unsigned int op)	\
267fe2f639SDominik Brodowski 	{						\
277fe2f639SDominik Brodowski 	unsigned int eax, ebx, ecx, edx;		\
287fe2f639SDominik Brodowski 	__cpuid(op, eax, ebx, ecx, edx);		\
297fe2f639SDominik Brodowski 	return reg;					\
307fe2f639SDominik Brodowski 	}
317fe2f639SDominik Brodowski cpuid_func(eax);
327fe2f639SDominik Brodowski cpuid_func(ebx);
337fe2f639SDominik Brodowski cpuid_func(ecx);
347fe2f639SDominik Brodowski cpuid_func(edx);
357fe2f639SDominik Brodowski 
367fe2f639SDominik Brodowski #endif /* defined(__i386__) || defined(__x86_64__) */
377fe2f639SDominik Brodowski 
387fe2f639SDominik Brodowski /* get_cpu_info
397fe2f639SDominik Brodowski  *
407fe2f639SDominik Brodowski  * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
417fe2f639SDominik Brodowski  *
427fe2f639SDominik Brodowski  * Returns 0 on success or a negativ error code
437fe2f639SDominik Brodowski  *
447fe2f639SDominik Brodowski  * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
457fe2f639SDominik Brodowski  */
46d0e4a193SPrarit Bhargava int get_cpu_info(struct cpupower_cpu_info *cpu_info)
477fe2f639SDominik Brodowski {
487fe2f639SDominik Brodowski 	FILE *fp;
497fe2f639SDominik Brodowski 	char value[64];
507fe2f639SDominik Brodowski 	unsigned int proc, x;
517fe2f639SDominik Brodowski 	unsigned int unknown = 0xffffff;
527fe2f639SDominik Brodowski 	unsigned int cpuid_level, ext_cpuid_level;
537fe2f639SDominik Brodowski 
547fe2f639SDominik Brodowski 	int ret = -EINVAL;
557fe2f639SDominik Brodowski 
567fe2f639SDominik Brodowski 	cpu_info->vendor		= X86_VENDOR_UNKNOWN;
577fe2f639SDominik Brodowski 	cpu_info->family		= unknown;
587fe2f639SDominik Brodowski 	cpu_info->model			= unknown;
597fe2f639SDominik Brodowski 	cpu_info->stepping		= unknown;
607fe2f639SDominik Brodowski 	cpu_info->caps			= 0;
617fe2f639SDominik Brodowski 
627fe2f639SDominik Brodowski 	fp = fopen("/proc/cpuinfo", "r");
637fe2f639SDominik Brodowski 	if (!fp)
647fe2f639SDominik Brodowski 		return -EIO;
657fe2f639SDominik Brodowski 
667fe2f639SDominik Brodowski 	while (!feof(fp)) {
677fe2f639SDominik Brodowski 		if (!fgets(value, 64, fp))
687fe2f639SDominik Brodowski 			continue;
697fe2f639SDominik Brodowski 		value[63 - 1] = '\0';
707fe2f639SDominik Brodowski 
712cd005caSDominik Brodowski 		if (!strncmp(value, "processor\t: ", 12))
727fe2f639SDominik Brodowski 			sscanf(value, "processor\t: %u", &proc);
732cd005caSDominik Brodowski 
74d0e4a193SPrarit Bhargava 		if (proc != (unsigned int)base_cpu)
757fe2f639SDominik Brodowski 			continue;
767fe2f639SDominik Brodowski 
777fe2f639SDominik Brodowski 		/* Get CPU vendor */
782cd005caSDominik Brodowski 		if (!strncmp(value, "vendor_id", 9)) {
797fe2f639SDominik Brodowski 			for (x = 1; x < X86_VENDOR_MAX; x++) {
807fe2f639SDominik Brodowski 				if (strstr(value, cpu_vendor_table[x]))
817fe2f639SDominik Brodowski 					cpu_info->vendor = x;
827fe2f639SDominik Brodowski 			}
837fe2f639SDominik Brodowski 		/* Get CPU family, etc. */
842cd005caSDominik Brodowski 		} else if (!strncmp(value, "cpu family\t: ", 13)) {
857fe2f639SDominik Brodowski 			sscanf(value, "cpu family\t: %u",
867fe2f639SDominik Brodowski 			       &cpu_info->family);
872cd005caSDominik Brodowski 		} else if (!strncmp(value, "model\t\t: ", 9)) {
887fe2f639SDominik Brodowski 			sscanf(value, "model\t\t: %u",
897fe2f639SDominik Brodowski 			       &cpu_info->model);
902cd005caSDominik Brodowski 		} else if (!strncmp(value, "stepping\t: ", 10)) {
917fe2f639SDominik Brodowski 			sscanf(value, "stepping\t: %u",
927fe2f639SDominik Brodowski 			       &cpu_info->stepping);
937fe2f639SDominik Brodowski 
947fe2f639SDominik Brodowski 			/* Exit -> all values must have been set */
957fe2f639SDominik Brodowski 			if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
967fe2f639SDominik Brodowski 			    cpu_info->family == unknown ||
977fe2f639SDominik Brodowski 			    cpu_info->model == unknown ||
987fe2f639SDominik Brodowski 			    cpu_info->stepping == unknown) {
997fe2f639SDominik Brodowski 				ret = -EINVAL;
1007fe2f639SDominik Brodowski 				goto out;
1017fe2f639SDominik Brodowski 			}
1027fe2f639SDominik Brodowski 
1037fe2f639SDominik Brodowski 			ret = 0;
1047fe2f639SDominik Brodowski 			goto out;
1057fe2f639SDominik Brodowski 		}
1067fe2f639SDominik Brodowski 	}
1077fe2f639SDominik Brodowski 	ret = -ENODEV;
1087fe2f639SDominik Brodowski out:
1097fe2f639SDominik Brodowski 	fclose(fp);
1107fe2f639SDominik Brodowski 	/* Get some useful CPU capabilities from cpuid */
1117fe2f639SDominik Brodowski 	if (cpu_info->vendor != X86_VENDOR_AMD &&
112995d5f64SPu Wen 	    cpu_info->vendor != X86_VENDOR_HYGON &&
1137fe2f639SDominik Brodowski 	    cpu_info->vendor != X86_VENDOR_INTEL)
1147fe2f639SDominik Brodowski 		return ret;
1157fe2f639SDominik Brodowski 
1167fe2f639SDominik Brodowski 	cpuid_level	= cpuid_eax(0);
1177fe2f639SDominik Brodowski 	ext_cpuid_level	= cpuid_eax(0x80000000);
1187fe2f639SDominik Brodowski 
1197fe2f639SDominik Brodowski 	/* Invariant TSC */
1207fe2f639SDominik Brodowski 	if (ext_cpuid_level >= 0x80000007 &&
1217fe2f639SDominik Brodowski 	    (cpuid_edx(0x80000007) & (1 << 8)))
1227fe2f639SDominik Brodowski 		cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
1237fe2f639SDominik Brodowski 
1247fe2f639SDominik Brodowski 	/* Aperf/Mperf registers support */
1257fe2f639SDominik Brodowski 	if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
1267fe2f639SDominik Brodowski 		cpu_info->caps |= CPUPOWER_CAP_APERF;
1277fe2f639SDominik Brodowski 
128995d5f64SPu Wen 	/* AMD or Hygon Boost state enable/disable register */
129995d5f64SPu Wen 	if (cpu_info->vendor == X86_VENDOR_AMD ||
130995d5f64SPu Wen 	    cpu_info->vendor == X86_VENDOR_HYGON) {
131a0255a76SNathan Fontenot 		if (ext_cpuid_level >= 0x80000007) {
132a0255a76SNathan Fontenot 			if (cpuid_edx(0x80000007) & (1 << 9))
1337a136a8fSRobert Richter 				cpu_info->caps |= CPUPOWER_CAP_AMD_CPB;
1346af2ed53SJanakarajan Natarajan 
135a0255a76SNathan Fontenot 			if ((cpuid_edx(0x80000007) & (1 << 7)) &&
136*23765b82SNathan Fontenot 			    cpu_info->family != 0x14) {
137a0255a76SNathan Fontenot 				/* HW pstate was not implemented in family 0x14 */
138a0255a76SNathan Fontenot 				cpu_info->caps |= CPUPOWER_CAP_AMD_HW_PSTATE;
139*23765b82SNathan Fontenot 
140*23765b82SNathan Fontenot 				if (cpu_info->family >= 0x17)
141*23765b82SNathan Fontenot 					cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATEDEF;
142*23765b82SNathan Fontenot 			}
143a0255a76SNathan Fontenot 		}
144a0255a76SNathan Fontenot 
1456af2ed53SJanakarajan Natarajan 		if (ext_cpuid_level >= 0x80000008 &&
1466af2ed53SJanakarajan Natarajan 		    cpuid_ebx(0x80000008) & (1 << 4))
1476af2ed53SJanakarajan Natarajan 			cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU;
1487fe2f639SDominik Brodowski 	}
1497fe2f639SDominik Brodowski 
1507fe2f639SDominik Brodowski 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
151029e9f73SThomas Renninger 		if (cpuid_level >= 6 &&
152029e9f73SThomas Renninger 		    (cpuid_eax(6) & (1 << 1)))
153029e9f73SThomas Renninger 			cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
154029e9f73SThomas Renninger 	}
155029e9f73SThomas Renninger 
156029e9f73SThomas Renninger 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
1578fb2e440SThomas Renninger 		/* Intel's perf-bias MSR support */
1587fe2f639SDominik Brodowski 		if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
1597fe2f639SDominik Brodowski 			cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
1608fb2e440SThomas Renninger 
1618fb2e440SThomas Renninger 		/* Intel's Turbo Ratio Limit support */
1628fb2e440SThomas Renninger 		if (cpu_info->family == 6) {
1638fb2e440SThomas Renninger 			switch (cpu_info->model) {
1648fb2e440SThomas Renninger 			case 0x1A:	/* Core i7, Xeon 5500 series
1658fb2e440SThomas Renninger 					 * Bloomfield, Gainstown NHM-EP
1668fb2e440SThomas Renninger 					 */
1678fb2e440SThomas Renninger 			case 0x1E:	/* Core i7 and i5 Processor
1688fb2e440SThomas Renninger 					 * Clarksfield, Lynnfield, Jasper Forest
1698fb2e440SThomas Renninger 					 */
1708fb2e440SThomas Renninger 			case 0x1F:	/* Core i7 and i5 Processor - Nehalem */
1718fb2e440SThomas Renninger 			case 0x25:	/* Westmere Client
1728fb2e440SThomas Renninger 					 * Clarkdale, Arrandale
1738fb2e440SThomas Renninger 					 */
1748fb2e440SThomas Renninger 			case 0x2C:	/* Westmere EP - Gulftown */
1758fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
1764cca0457SBen Hutchings 				break;
1778fb2e440SThomas Renninger 			case 0x2A:	/* SNB */
1788fb2e440SThomas Renninger 			case 0x2D:	/* SNB Xeon */
1798d219e36SThomas Renninger 			case 0x3A:	/* IVB */
1808d219e36SThomas Renninger 			case 0x3E:	/* IVB Xeon */
1818fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
1828fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
1838fb2e440SThomas Renninger 				break;
1848fb2e440SThomas Renninger 			case 0x2E:	/* Nehalem-EX Xeon - Beckton */
1858fb2e440SThomas Renninger 			case 0x2F:	/* Westmere-EX Xeon - Eagleton */
1868fb2e440SThomas Renninger 			default:
1878fb2e440SThomas Renninger 				break;
1888fb2e440SThomas Renninger 			}
1898fb2e440SThomas Renninger 		}
1907fe2f639SDominik Brodowski 	}
1917fe2f639SDominik Brodowski 
1927fe2f639SDominik Brodowski 	/*	printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
1937fe2f639SDominik Brodowski 		cpuid_level, ext_cpuid_level, cpu_info->caps);
1947fe2f639SDominik Brodowski 	*/
1957fe2f639SDominik Brodowski 	return ret;
1967fe2f639SDominik Brodowski }
197