summaryrefslogtreecommitdiff
path: root/gprofng/common/cpuid.c
blob: fd98b30c63982b1b266cc1ddc451c47ae4f688de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* Copyright (C) 2021-2023 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#if defined(__i386__) || defined(__x86_64)
#include <cpuid.h>  /* GCC-provided */
#elif defined(__aarch64__)
#define ATTRIBUTE_UNUSED __attribute__((unused))

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)
{
  // CPUID bit assignments:
  // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
  // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
  // [19:16] Constant (Reads as 0xF)
  // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
  // [03:00] REVISION indicates patch release (0x0 = Patch 0)
  //    unsigned long v = 0;
  //    __asm volatile ("MRS %[result], MPIDR_EL1" : [result] "=r" (v));
  //    Tprintf(DBG_LT0, "cpuid.c:%d read_cpuid_id() MPIDR_EL1=0x%016lx\n", __LINE__, v);
  uint_t res = 0;
  __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (*eax));
  Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1=0x%016x\n", __LINE__, *eax);
  return res;
}
#endif

/*
 * Various routines to handle identification
 * and classification of x86 processors.
 */

#define IS_GLOBAL /* externally visible */
#define	X86_VENDOR_Intel	0
#define	X86_VENDORSTR_Intel	"GenuineIntel"
#define	X86_VENDOR_IntelClone	1
#define	X86_VENDOR_AMD		2
#define	X86_VENDORSTR_AMD	"AuthenticAMD"

#define BITX(u, h, l)       (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
#define CPI_FAMILY_XTD(reg) BITX(reg, 27, 20)
#define CPI_MODEL_XTD(reg)  BITX(reg, 19, 16)
#define CPI_TYPE(reg)       BITX(reg, 13, 12)
#define CPI_FAMILY(reg)     BITX(reg, 11, 8)
#define CPI_STEP(reg)       BITX(reg, 3, 0)
#define CPI_MODEL(reg)      BITX(reg, 7, 4)
#define IS_EXTENDED_MODEL_INTEL(model)  ((model) == 0x6 || (model) >= 0xf)


typedef struct
{
  unsigned int eax;
  unsigned int ebx;
  unsigned int ecx;
  unsigned int edx;
} cpuid_regs_t;

typedef struct
{
  unsigned int cpi_model;
  unsigned int cpi_family;
  unsigned int cpi_vendor;        /* enum of cpi_vendorstr */
  unsigned int cpi_maxeax;        /* fn 0: %eax */
  char cpi_vendorstr[13];         /* fn 0: %ebx:%ecx:%edx */
} cpuid_info_t;


#if defined(__i386__) || defined(__x86_64)
static uint_t
cpuid_vendorstr_to_vendorcode (char *vendorstr)
{
  if (strcmp (vendorstr, X86_VENDORSTR_Intel) == 0)
    return X86_VENDOR_Intel;
  else if (strcmp (vendorstr, X86_VENDORSTR_AMD) == 0)
    return X86_VENDOR_AMD;
  else
    return X86_VENDOR_IntelClone;
}

static int
my_cpuid (unsigned int op, cpuid_regs_t *regs)
{
  regs->eax = regs->ebx = regs->ecx = regs->edx = 0;
  int ret = __get_cpuid (op, &regs->eax, &regs->ebx, &regs->ecx, &regs->edx);
  TprintfT (DBG_LT1, "my_cpuid: __get_cpuid(0x%x, 0x%x, 0x%x, 0x%x, 0x%x) returns %d\n",
	    op, regs->eax, regs->ebx, regs->ecx, regs->edx, ret);
  return ret;
}
#endif

static cpuid_info_t *
get_cpuid_info ()
{
  static int cpuid_inited = 0;
  static cpuid_info_t cpuid_info;
  cpuid_info_t *cpi = &cpuid_info;
  if (cpuid_inited)
    return cpi;
  cpuid_inited = 1;

#if defined(__aarch64__)
  // CPUID bit assignments:
  // [31:24] IMPLEMENTER (0x50 - ARM_CPU_IMP_APM)
  // [23:20] VARIANT indicates processor revision (0x2 = Revision 2)
  // [19:16] Constant (Reads as 0xF)
  // [15:04] PARTNO indicates part number (0xC23 = Cortex-M3)
  // [03:00] REVISION indicates patch release (0x0 = Patch 0)
  uint_t reg = 0;
  __asm volatile ("MRS %[result], MIDR_EL1" : [result] "=r" (reg));
  cpi->cpi_vendor = reg >> 24;
  cpi->cpi_model = (reg >> 4) & 0xfff;
  switch (cpi->cpi_vendor)
    {
    case ARM_CPU_IMP_APM:
    case ARM_CPU_IMP_ARM:
    case ARM_CPU_IMP_CAVIUM:
    case ARM_CPU_IMP_BRCM:
    case ARM_CPU_IMP_QCOM:
      strncpy (cpi->cpi_vendorstr, AARCH64_VENDORSTR_ARM, sizeof (cpi->cpi_vendorstr));
      break;
    default:
      strncpy (cpi->cpi_vendorstr, "UNKNOWN ARM", sizeof (cpi->cpi_vendorstr));
      break;
    }
  Tprintf (DBG_LT0, "cpuid.c:%d read_cpuid_id() MIDR_EL1==0x%016x cpi_vendor=%d cpi_model=%d\n",
	   __LINE__, (unsigned int) reg, cpi->cpi_vendor, cpi->cpi_model);

#elif defined(__i386__) || defined(__x86_64)
  cpuid_regs_t regs;
  my_cpuid (0, &regs);
  cpi->cpi_maxeax = regs.eax;
  ((uint32_t *) cpi->cpi_vendorstr)[0] = regs.ebx;
  ((uint32_t *) cpi->cpi_vendorstr)[1] = regs.edx;
  ((uint32_t *) cpi->cpi_vendorstr)[2] = regs.ecx;
  cpi->cpi_vendorstr[12] = 0;
  cpi->cpi_vendor = cpuid_vendorstr_to_vendorcode (cpi->cpi_vendorstr);

  my_cpuid (1, &regs);
  cpi->cpi_model = CPI_MODEL (regs.eax);
  cpi->cpi_family = CPI_FAMILY (regs.eax);
  if (cpi->cpi_family == 0xf)
    cpi->cpi_family += CPI_FAMILY_XTD (regs.eax);

  /*
   * Beware: AMD uses "extended model" iff base *FAMILY* == 0xf.
   * Intel, and presumably everyone else, uses model == 0xf, as
   * one would expect (max value means possible overflow).  Sigh.
   */
  switch (cpi->cpi_vendor)
    {
    case X86_VENDOR_Intel:
      if (IS_EXTENDED_MODEL_INTEL (cpi->cpi_family))
	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
      break;
    case X86_VENDOR_AMD:
      if (CPI_FAMILY (cpi->cpi_family) == 0xf)
	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
      break;
    default:
      if (cpi->cpi_model == 0xf)
	cpi->cpi_model += CPI_MODEL_XTD (regs.eax) << 4;
      break;
    }
#endif
  return cpi;
}

static inline uint_t
cpuid_getvendor ()
{
  return get_cpuid_info ()->cpi_vendor;
}

static inline uint_t
cpuid_getfamily ()
{
  return get_cpuid_info ()->cpi_family;
}

static inline uint_t
cpuid_getmodel ()
{
  return get_cpuid_info ()->cpi_model;
}