cpuid.c 4.4 KB

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