cpuid.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /* Copyright (C) 2021 Free Software Foundation, Inc.
  2. Contributed by Oracle.
  3. This file is part of GNU Binutils.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3, or (at your option)
  7. any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, 51 Franklin Street - Fifth Floor, Boston,
  15. MA 02110-1301, USA. */
  16. #if defined(__i386__) || defined(__x86_64)
  17. #include <cpuid.h> /* GCC-provided */
  18. #elif defined(__aarch64__)
  19. #define ATTRIBUTE_UNUSED __attribute__((unused))
  20. static inline uint_t __attribute_const__
  21. __get_cpuid (unsigned int op ATTRIBUTE_UNUSED, unsigned int *eax,
  22. unsigned int *ebx ATTRIBUTE_UNUSED,
  23. unsigned int *ecx ATTRIBUTE_UNUSED, unsigned int *edx ATTRIBUTE_UNUSED)
  24. {
  25. // CPUID bit assignments:
  26. // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
  27. // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
  28. // [19:16] Constant (Reads as 0xF)
  29. // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
  30. // [03:00] REVISION indicates patch release (0x0 = Patch 0)
  31. // unsigned long v = 0;
  32. // __asm volatile ("MRS %[result], MPIDR_EL1" : [result] "=r" (v));
  33. // Tprintf(DBG_LT0, "cpuid.c:%d read_cpuid_id() MPIDR_EL1=0x%016lx\n", __LINE__, v);
  34. uint_t res = 0;
  35. __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (*eax));
  36. Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1=0x%016x\n", __LINE__, *eax);
  37. return res;
  38. }
  39. #endif
  40. /*
  41. * Various routines to handle identification
  42. * and classification of x86 processors.
  43. */
  44. #define IS_GLOBAL /* externally visible */
  45. #define X86_VENDOR_Intel 0
  46. #define X86_VENDORSTR_Intel "GenuineIntel"
  47. #define X86_VENDOR_IntelClone 1
  48. #define X86_VENDOR_AMD 2
  49. #define X86_VENDORSTR_AMD "AuthenticAMD"
  50. #define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
  51. #define CPI_FAMILY_XTD(reg) BITX(reg, 27, 20)
  52. #define CPI_MODEL_XTD(reg) BITX(reg, 19, 16)
  53. #define CPI_TYPE(reg) BITX(reg, 13, 12)
  54. #define CPI_FAMILY(reg) BITX(reg, 11, 8)
  55. #define CPI_STEP(reg) BITX(reg, 3, 0)
  56. #define CPI_MODEL(reg) BITX(reg, 7, 4)
  57. #define IS_EXTENDED_MODEL_INTEL(model) ((model) == 0x6 || (model) >= 0xf)
  58. typedef struct
  59. {
  60. unsigned int eax;
  61. unsigned int ebx;
  62. unsigned int ecx;
  63. unsigned int edx;
  64. } cpuid_regs_t;
  65. typedef struct
  66. {
  67. unsigned int cpi_model;
  68. unsigned int cpi_family;
  69. unsigned int cpi_vendor; /* enum of cpi_vendorstr */
  70. unsigned int cpi_maxeax; /* fn 0: %eax */
  71. char cpi_vendorstr[13]; /* fn 0: %ebx:%ecx:%edx */
  72. } cpuid_info_t;
  73. #if defined(__i386__) || defined(__x86_64)
  74. static uint_t
  75. cpuid_vendorstr_to_vendorcode (char *vendorstr)
  76. {
  77. if (strcmp (vendorstr, X86_VENDORSTR_Intel) == 0)
  78. return X86_VENDOR_Intel;
  79. else if (strcmp (vendorstr, X86_VENDORSTR_AMD) == 0)
  80. return X86_VENDOR_AMD;
  81. else
  82. return X86_VENDOR_IntelClone;
  83. }
  84. static int
  85. my_cpuid (unsigned int op, cpuid_regs_t *regs)
  86. {
  87. regs->eax = regs->ebx = regs->ecx = regs->edx = 0;
  88. int ret = __get_cpuid (op, &regs->eax, &regs->ebx, &regs->ecx, &regs->edx);
  89. TprintfT (DBG_LT1, "my_cpuid: __get_cpuid(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) returns %d\n",
  90. op, regs->eax, regs->ebx, regs->ecx, regs->edx, ret);
  91. return ret;
  92. }
  93. #endif
  94. static cpuid_info_t *
  95. get_cpuid_info ()
  96. {
  97. static int cpuid_inited = 0;
  98. static cpuid_info_t cpuid_info;
  99. cpuid_info_t *cpi = &cpuid_info;
  100. if (cpuid_inited)
  101. return cpi;
  102. cpuid_inited = 1;
  103. #if defined(__aarch64__)
  104. // CPUID bit assignments:
  105. // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
  106. // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
  107. // [19:16] Constant (Reads as 0xF)
  108. // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
  109. // [03:00] REVISION indicates patch release (0x0 = Patch 0)
  110. uint_t reg = 0;
  111. __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (reg));
  112. cpi->cpi_vendor = reg >> 24;
  113. cpi->cpi_model = (reg >> 4) & 0xfff;
  114. switch (cpi->cpi_vendor)
  115. {
  116. case ARM_CPU_IMP_APM:
  117. case ARM_CPU_IMP_ARM:
  118. case ARM_CPU_IMP_CAVIUM:
  119. case ARM_CPU_IMP_BRCM:
  120. case ARM_CPU_IMP_QCOM:
  121. strncpy (cpi->cpi_vendorstr, AARCH64_VENDORSTR_ARM, sizeof (cpi->cpi_vendorstr));
  122. break;
  123. default:
  124. strncpy (cpi->cpi_vendorstr, "UNKNOWN ARM", sizeof (cpi->cpi_vendorstr));
  125. break;
  126. }
  127. Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1==0x%016x cpi_vendor=%d cpi_model=%d\n",
  128. __LINE__, (unsigned int) reg, cpi->cpi_vendor, cpi->cpi_model);
  129. #elif defined(__i386__) || defined(__x86_64)
  130. cpuid_regs_t regs;
  131. my_cpuid (0, &regs);
  132. cpi->cpi_maxeax = regs.eax;
  133. ((uint32_t *) cpi->cpi_vendorstr)[0] = regs.ebx;
  134. ((uint32_t *) cpi->cpi_vendorstr)[1] = regs.edx;
  135. ((uint32_t *) cpi->cpi_vendorstr)[2] = regs.ecx;
  136. cpi->cpi_vendorstr[12] = 0;
  137. cpi->cpi_vendor = cpuid_vendorstr_to_vendorcode (cpi->cpi_vendorstr);
  138. my_cpuid (1, &regs);
  139. cpi->cpi_model = CPI_MODEL (regs.eax);
  140. cpi->cpi_family = CPI_FAMILY (regs.eax);
  141. if (cpi->cpi_family == 0xf)
  142. cpi->cpi_family += CPI_FAMILY_XTD (regs.eax);
  143. /*
  144. * Beware: AMD uses "extended model" iff base *FAMILY* == 0xf.
  145. * Intel, and presumably everyone else, uses model == 0xf, as
  146. * one would expect (max value means possible overflow). Sigh.
  147. */
  148. switch (cpi->cpi_vendor)
  149. {
  150. case X86_VENDOR_Intel:
  151. if (IS_EXTENDED_MODEL_INTEL (cpi->cpi_family))
  152. cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
  153. break;
  154. case X86_VENDOR_AMD:
  155. if (CPI_FAMILY (cpi->cpi_family) == 0xf)
  156. cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
  157. break;
  158. default:
  159. if (cpi->cpi_model == 0xf)
  160. cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
  161. break;
  162. }
  163. #endif
  164. return cpi;
  165. }
  166. static inline uint_t
  167. cpuid_getvendor ()
  168. {
  169. return get_cpuid_info ()->cpi_vendor;
  170. }
  171. static inline uint_t
  172. cpuid_getfamily ()
  173. {
  174. return get_cpuid_info ()->cpi_family;
  175. }
  176. static inline uint_t
  177. cpuid_getmodel ()
  178. {
  179. return get_cpuid_info ()->cpi_model;
  180. }