cpuid.c revision 1.1.1.3 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