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