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
26static 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
69typedef struct
70{
71  unsigned int eax;
72  unsigned int ebx;
73  unsigned int ecx;
74  unsigned int edx;
75} cpuid_regs_t;
76
77typedef 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)
88static uint_t
89cpuid_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
99static int
100my_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
110static cpuid_info_t *
111get_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
187static inline uint_t
188cpuid_getvendor ()
189{
190  return get_cpuid_info ()->cpi_vendor;
191}
192
193static inline uint_t
194cpuid_getfamily ()
195{
196  return get_cpuid_info ()->cpi_family;
197}
198
199static inline uint_t
200cpuid_getmodel ()
201{
202  return get_cpuid_info ()->cpi_model;
203}
204