xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/common/cpuid.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
1 /* Copyright (C) 2021-2024 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__
__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 __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
cpuid_vendorstr_to_vendorcode(char * vendorstr)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
my_cpuid(unsigned int op,cpuid_regs_t * regs)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, &regs->eax, &regs->ebx, &regs->ecx, &regs->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 *
get_cpuid_info()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, &regs);
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, &regs);
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
cpuid_getvendor()188 cpuid_getvendor ()
189 {
190   return get_cpuid_info ()->cpi_vendor;
191 }
192 
193 static inline uint_t
cpuid_getfamily()194 cpuid_getfamily ()
195 {
196   return get_cpuid_info ()->cpi_family;
197 }
198 
199 static inline uint_t
cpuid_getmodel()200 cpuid_getmodel ()
201 {
202   return get_cpuid_info ()->cpi_model;
203 }
204