xref: /linux/tools/power/cpupower/utils/helpers/cpuid.c (revision 995d5f64b62f20f05b8e0972f07ec4d6c23333c9)
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] = {
11*995d5f64SPu 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 &&
112*995d5f64SPu 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 
128*995d5f64SPu Wen 	/* AMD or Hygon Boost state enable/disable register */
129*995d5f64SPu Wen 	if (cpu_info->vendor == X86_VENDOR_AMD ||
130*995d5f64SPu Wen 	    cpu_info->vendor == X86_VENDOR_HYGON) {
1317fe2f639SDominik Brodowski 		if (ext_cpuid_level >= 0x80000007 &&
1327fe2f639SDominik Brodowski 		    (cpuid_edx(0x80000007) & (1 << 9)))
1337fe2f639SDominik Brodowski 			cpu_info->caps |= CPUPOWER_CAP_AMD_CBP;
1347fe2f639SDominik Brodowski 	}
1357fe2f639SDominik Brodowski 
1367fe2f639SDominik Brodowski 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
137029e9f73SThomas Renninger 		if (cpuid_level >= 6 &&
138029e9f73SThomas Renninger 		    (cpuid_eax(6) & (1 << 1)))
139029e9f73SThomas Renninger 			cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
140029e9f73SThomas Renninger 	}
141029e9f73SThomas Renninger 
142029e9f73SThomas Renninger 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
1438fb2e440SThomas Renninger 		/* Intel's perf-bias MSR support */
1447fe2f639SDominik Brodowski 		if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
1457fe2f639SDominik Brodowski 			cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
1468fb2e440SThomas Renninger 
1478fb2e440SThomas Renninger 		/* Intel's Turbo Ratio Limit support */
1488fb2e440SThomas Renninger 		if (cpu_info->family == 6) {
1498fb2e440SThomas Renninger 			switch (cpu_info->model) {
1508fb2e440SThomas Renninger 			case 0x1A:	/* Core i7, Xeon 5500 series
1518fb2e440SThomas Renninger 					 * Bloomfield, Gainstown NHM-EP
1528fb2e440SThomas Renninger 					 */
1538fb2e440SThomas Renninger 			case 0x1E:	/* Core i7 and i5 Processor
1548fb2e440SThomas Renninger 					 * Clarksfield, Lynnfield, Jasper Forest
1558fb2e440SThomas Renninger 					 */
1568fb2e440SThomas Renninger 			case 0x1F:	/* Core i7 and i5 Processor - Nehalem */
1578fb2e440SThomas Renninger 			case 0x25:	/* Westmere Client
1588fb2e440SThomas Renninger 					 * Clarkdale, Arrandale
1598fb2e440SThomas Renninger 					 */
1608fb2e440SThomas Renninger 			case 0x2C:	/* Westmere EP - Gulftown */
1618fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
1624cca0457SBen Hutchings 				break;
1638fb2e440SThomas Renninger 			case 0x2A:	/* SNB */
1648fb2e440SThomas Renninger 			case 0x2D:	/* SNB Xeon */
1658d219e36SThomas Renninger 			case 0x3A:	/* IVB */
1668d219e36SThomas Renninger 			case 0x3E:	/* IVB Xeon */
1678fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
1688fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
1698fb2e440SThomas Renninger 				break;
1708fb2e440SThomas Renninger 			case 0x2E:	/* Nehalem-EX Xeon - Beckton */
1718fb2e440SThomas Renninger 			case 0x2F:	/* Westmere-EX Xeon - Eagleton */
1728fb2e440SThomas Renninger 			default:
1738fb2e440SThomas Renninger 				break;
1748fb2e440SThomas Renninger 			}
1758fb2e440SThomas Renninger 		}
1767fe2f639SDominik Brodowski 	}
1777fe2f639SDominik Brodowski 
1787fe2f639SDominik Brodowski 	/*	printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
1797fe2f639SDominik Brodowski 		cpuid_level, ext_cpuid_level, cpu_info->caps);
1807fe2f639SDominik Brodowski 	*/
1817fe2f639SDominik Brodowski 	return ret;
1827fe2f639SDominik Brodowski }
183