xref: /linux/tools/power/cpupower/utils/helpers/cpuid.c (revision 4cca0457686e4ee1677d69469e4ddfd94d389a80)
17fe2f639SDominik Brodowski #include <stdio.h>
27fe2f639SDominik Brodowski #include <errno.h>
37fe2f639SDominik Brodowski #include <string.h>
47fe2f639SDominik Brodowski #include <unistd.h>
57fe2f639SDominik Brodowski #include <stdlib.h>
67fe2f639SDominik Brodowski 
77fe2f639SDominik Brodowski #include "helpers/helpers.h"
87fe2f639SDominik Brodowski 
97fe2f639SDominik Brodowski static const char *cpu_vendor_table[X86_VENDOR_MAX] = {
107fe2f639SDominik Brodowski 	"Unknown", "GenuineIntel", "AuthenticAMD",
117fe2f639SDominik Brodowski };
127fe2f639SDominik Brodowski 
137fe2f639SDominik Brodowski #if defined(__i386__) || defined(__x86_64__)
147fe2f639SDominik Brodowski 
157fe2f639SDominik Brodowski /* from gcc */
167fe2f639SDominik Brodowski #include <cpuid.h>
177fe2f639SDominik Brodowski 
187fe2f639SDominik Brodowski /*
197fe2f639SDominik Brodowski  * CPUID functions returning a single datum
207fe2f639SDominik Brodowski  *
217fe2f639SDominik Brodowski  * Define unsigned int cpuid_e[abcd]x(unsigned int op)
227fe2f639SDominik Brodowski  */
237fe2f639SDominik Brodowski #define cpuid_func(reg)					\
247fe2f639SDominik Brodowski 	unsigned int cpuid_##reg(unsigned int op)	\
257fe2f639SDominik Brodowski 	{						\
267fe2f639SDominik Brodowski 	unsigned int eax, ebx, ecx, edx;		\
277fe2f639SDominik Brodowski 	__cpuid(op, eax, ebx, ecx, edx);		\
287fe2f639SDominik Brodowski 	return reg;					\
297fe2f639SDominik Brodowski 	}
307fe2f639SDominik Brodowski cpuid_func(eax);
317fe2f639SDominik Brodowski cpuid_func(ebx);
327fe2f639SDominik Brodowski cpuid_func(ecx);
337fe2f639SDominik Brodowski cpuid_func(edx);
347fe2f639SDominik Brodowski 
357fe2f639SDominik Brodowski #endif /* defined(__i386__) || defined(__x86_64__) */
367fe2f639SDominik Brodowski 
377fe2f639SDominik Brodowski /* get_cpu_info
387fe2f639SDominik Brodowski  *
397fe2f639SDominik Brodowski  * Extract CPU vendor, family, model, stepping info from /proc/cpuinfo
407fe2f639SDominik Brodowski  *
417fe2f639SDominik Brodowski  * Returns 0 on success or a negativ error code
427fe2f639SDominik Brodowski  *
437fe2f639SDominik Brodowski  * TBD: Should there be a cpuid alternative for this if /proc is not mounted?
447fe2f639SDominik Brodowski  */
457fe2f639SDominik Brodowski int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info)
467fe2f639SDominik Brodowski {
477fe2f639SDominik Brodowski 	FILE *fp;
487fe2f639SDominik Brodowski 	char value[64];
497fe2f639SDominik Brodowski 	unsigned int proc, x;
507fe2f639SDominik Brodowski 	unsigned int unknown = 0xffffff;
517fe2f639SDominik Brodowski 	unsigned int cpuid_level, ext_cpuid_level;
527fe2f639SDominik Brodowski 
537fe2f639SDominik Brodowski 	int ret = -EINVAL;
547fe2f639SDominik Brodowski 
557fe2f639SDominik Brodowski 	cpu_info->vendor		= X86_VENDOR_UNKNOWN;
567fe2f639SDominik Brodowski 	cpu_info->family		= unknown;
577fe2f639SDominik Brodowski 	cpu_info->model			= unknown;
587fe2f639SDominik Brodowski 	cpu_info->stepping		= unknown;
597fe2f639SDominik Brodowski 	cpu_info->caps			= 0;
607fe2f639SDominik Brodowski 
617fe2f639SDominik Brodowski 	fp = fopen("/proc/cpuinfo", "r");
627fe2f639SDominik Brodowski 	if (!fp)
637fe2f639SDominik Brodowski 		return -EIO;
647fe2f639SDominik Brodowski 
657fe2f639SDominik Brodowski 	while (!feof(fp)) {
667fe2f639SDominik Brodowski 		if (!fgets(value, 64, fp))
677fe2f639SDominik Brodowski 			continue;
687fe2f639SDominik Brodowski 		value[63 - 1] = '\0';
697fe2f639SDominik Brodowski 
702cd005caSDominik Brodowski 		if (!strncmp(value, "processor\t: ", 12))
717fe2f639SDominik Brodowski 			sscanf(value, "processor\t: %u", &proc);
722cd005caSDominik Brodowski 
737fe2f639SDominik Brodowski 		if (proc != cpu)
747fe2f639SDominik Brodowski 			continue;
757fe2f639SDominik Brodowski 
767fe2f639SDominik Brodowski 		/* Get CPU vendor */
772cd005caSDominik Brodowski 		if (!strncmp(value, "vendor_id", 9)) {
787fe2f639SDominik Brodowski 			for (x = 1; x < X86_VENDOR_MAX; x++) {
797fe2f639SDominik Brodowski 				if (strstr(value, cpu_vendor_table[x]))
807fe2f639SDominik Brodowski 					cpu_info->vendor = x;
817fe2f639SDominik Brodowski 			}
827fe2f639SDominik Brodowski 		/* Get CPU family, etc. */
832cd005caSDominik Brodowski 		} else if (!strncmp(value, "cpu family\t: ", 13)) {
847fe2f639SDominik Brodowski 			sscanf(value, "cpu family\t: %u",
857fe2f639SDominik Brodowski 			       &cpu_info->family);
862cd005caSDominik Brodowski 		} else if (!strncmp(value, "model\t\t: ", 9)) {
877fe2f639SDominik Brodowski 			sscanf(value, "model\t\t: %u",
887fe2f639SDominik Brodowski 			       &cpu_info->model);
892cd005caSDominik Brodowski 		} else if (!strncmp(value, "stepping\t: ", 10)) {
907fe2f639SDominik Brodowski 			sscanf(value, "stepping\t: %u",
917fe2f639SDominik Brodowski 			       &cpu_info->stepping);
927fe2f639SDominik Brodowski 
937fe2f639SDominik Brodowski 			/* Exit -> all values must have been set */
947fe2f639SDominik Brodowski 			if (cpu_info->vendor == X86_VENDOR_UNKNOWN ||
957fe2f639SDominik Brodowski 			    cpu_info->family == unknown ||
967fe2f639SDominik Brodowski 			    cpu_info->model == unknown ||
977fe2f639SDominik Brodowski 			    cpu_info->stepping == unknown) {
987fe2f639SDominik Brodowski 				ret = -EINVAL;
997fe2f639SDominik Brodowski 				goto out;
1007fe2f639SDominik Brodowski 			}
1017fe2f639SDominik Brodowski 
1027fe2f639SDominik Brodowski 			ret = 0;
1037fe2f639SDominik Brodowski 			goto out;
1047fe2f639SDominik Brodowski 		}
1057fe2f639SDominik Brodowski 	}
1067fe2f639SDominik Brodowski 	ret = -ENODEV;
1077fe2f639SDominik Brodowski out:
1087fe2f639SDominik Brodowski 	fclose(fp);
1097fe2f639SDominik Brodowski 	/* Get some useful CPU capabilities from cpuid */
1107fe2f639SDominik Brodowski 	if (cpu_info->vendor != X86_VENDOR_AMD &&
1117fe2f639SDominik Brodowski 	    cpu_info->vendor != X86_VENDOR_INTEL)
1127fe2f639SDominik Brodowski 		return ret;
1137fe2f639SDominik Brodowski 
1147fe2f639SDominik Brodowski 	cpuid_level	= cpuid_eax(0);
1157fe2f639SDominik Brodowski 	ext_cpuid_level	= cpuid_eax(0x80000000);
1167fe2f639SDominik Brodowski 
1177fe2f639SDominik Brodowski 	/* Invariant TSC */
1187fe2f639SDominik Brodowski 	if (ext_cpuid_level >= 0x80000007 &&
1197fe2f639SDominik Brodowski 	    (cpuid_edx(0x80000007) & (1 << 8)))
1207fe2f639SDominik Brodowski 		cpu_info->caps |= CPUPOWER_CAP_INV_TSC;
1217fe2f639SDominik Brodowski 
1227fe2f639SDominik Brodowski 	/* Aperf/Mperf registers support */
1237fe2f639SDominik Brodowski 	if (cpuid_level >= 6 && (cpuid_ecx(6) & 0x1))
1247fe2f639SDominik Brodowski 		cpu_info->caps |= CPUPOWER_CAP_APERF;
1257fe2f639SDominik Brodowski 
1267fe2f639SDominik Brodowski 	/* AMD Boost state enable/disable register */
1277fe2f639SDominik Brodowski 	if (cpu_info->vendor == X86_VENDOR_AMD) {
1287fe2f639SDominik Brodowski 		if (ext_cpuid_level >= 0x80000007 &&
1297fe2f639SDominik Brodowski 		    (cpuid_edx(0x80000007) & (1 << 9)))
1307fe2f639SDominik Brodowski 			cpu_info->caps |= CPUPOWER_CAP_AMD_CBP;
1317fe2f639SDominik Brodowski 	}
1327fe2f639SDominik Brodowski 
1337fe2f639SDominik Brodowski 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
134029e9f73SThomas Renninger 		if (cpuid_level >= 6 &&
135029e9f73SThomas Renninger 		    (cpuid_eax(6) & (1 << 1)))
136029e9f73SThomas Renninger 			cpu_info->caps |= CPUPOWER_CAP_INTEL_IDA;
137029e9f73SThomas Renninger 	}
138029e9f73SThomas Renninger 
139029e9f73SThomas Renninger 	if (cpu_info->vendor == X86_VENDOR_INTEL) {
1408fb2e440SThomas Renninger 		/* Intel's perf-bias MSR support */
1417fe2f639SDominik Brodowski 		if (cpuid_level >= 6 && (cpuid_ecx(6) & (1 << 3)))
1427fe2f639SDominik Brodowski 			cpu_info->caps |= CPUPOWER_CAP_PERF_BIAS;
1438fb2e440SThomas Renninger 
1448fb2e440SThomas Renninger 		/* Intel's Turbo Ratio Limit support */
1458fb2e440SThomas Renninger 		if (cpu_info->family == 6) {
1468fb2e440SThomas Renninger 			switch (cpu_info->model) {
1478fb2e440SThomas Renninger 			case 0x1A:	/* Core i7, Xeon 5500 series
1488fb2e440SThomas Renninger 					 * Bloomfield, Gainstown NHM-EP
1498fb2e440SThomas Renninger 					 */
1508fb2e440SThomas Renninger 			case 0x1E:	/* Core i7 and i5 Processor
1518fb2e440SThomas Renninger 					 * Clarksfield, Lynnfield, Jasper Forest
1528fb2e440SThomas Renninger 					 */
1538fb2e440SThomas Renninger 			case 0x1F:	/* Core i7 and i5 Processor - Nehalem */
1548fb2e440SThomas Renninger 			case 0x25:	/* Westmere Client
1558fb2e440SThomas Renninger 					 * Clarkdale, Arrandale
1568fb2e440SThomas Renninger 					 */
1578fb2e440SThomas Renninger 			case 0x2C:	/* Westmere EP - Gulftown */
1588fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
159*4cca0457SBen Hutchings 				break;
1608fb2e440SThomas Renninger 			case 0x2A:	/* SNB */
1618fb2e440SThomas Renninger 			case 0x2D:	/* SNB Xeon */
1628d219e36SThomas Renninger 			case 0x3A:	/* IVB */
1638d219e36SThomas Renninger 			case 0x3E:	/* IVB Xeon */
1648fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO;
1658fb2e440SThomas Renninger 				cpu_info->caps |= CPUPOWER_CAP_IS_SNB;
1668fb2e440SThomas Renninger 				break;
1678fb2e440SThomas Renninger 			case 0x2E:	/* Nehalem-EX Xeon - Beckton */
1688fb2e440SThomas Renninger 			case 0x2F:	/* Westmere-EX Xeon - Eagleton */
1698fb2e440SThomas Renninger 			default:
1708fb2e440SThomas Renninger 				break;
1718fb2e440SThomas Renninger 			}
1728fb2e440SThomas Renninger 		}
1737fe2f639SDominik Brodowski 	}
1747fe2f639SDominik Brodowski 
1757fe2f639SDominik Brodowski 	/*	printf("ID: %u - Extid: 0x%x - Caps: 0x%llx\n",
1767fe2f639SDominik Brodowski 		cpuid_level, ext_cpuid_level, cpu_info->caps);
1777fe2f639SDominik Brodowski 	*/
1787fe2f639SDominik Brodowski 	return ret;
1797fe2f639SDominik Brodowski }
180