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