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