]>
Commit | Line | Data |
---|---|---|
744d62ee | 1 | /* |
8229df20 | 2 | * lscpu-arm.c - ARM CPU identification tables |
744d62ee RV |
3 | * |
4 | * Copyright (C) 2018 Riku Voipio <riku.voipio@iki.fi> | |
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 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it would 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 along | |
17 | * with this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | * | |
20 | * The information here is gathered from | |
21 | * - ARM manuals | |
22 | * - Linux kernel: arch/armX/include/asm/cputype.h | |
23 | * - GCC sources: config/arch/arch-cores.def | |
24 | * - Ancient wisdom | |
367c85c4 | 25 | * - SMBIOS tables (if applicable) |
744d62ee | 26 | */ |
095be2c2 | 27 | #include "lscpu.h" |
744d62ee RV |
28 | |
29 | struct id_part { | |
30 | const int id; | |
31 | const char* name; | |
32 | }; | |
33 | ||
34 | static const struct id_part arm_part[] = { | |
35 | { 0x810, "ARM810" }, | |
36 | { 0x920, "ARM920" }, | |
37 | { 0x922, "ARM922" }, | |
38 | { 0x926, "ARM926" }, | |
39 | { 0x940, "ARM940" }, | |
40 | { 0x946, "ARM946" }, | |
41 | { 0x966, "ARM966" }, | |
42 | { 0xa20, "ARM1020" }, | |
43 | { 0xa22, "ARM1022" }, | |
44 | { 0xa26, "ARM1026" }, | |
45 | { 0xb02, "ARM11 MPCore" }, | |
46 | { 0xb36, "ARM1136" }, | |
47 | { 0xb56, "ARM1156" }, | |
48 | { 0xb76, "ARM1176" }, | |
49 | { 0xc05, "Cortex-A5" }, | |
50 | { 0xc07, "Cortex-A7" }, | |
51 | { 0xc08, "Cortex-A8" }, | |
52 | { 0xc09, "Cortex-A9" }, | |
dfd3bb8b | 53 | { 0xc0d, "Cortex-A17" }, /* Originally A12 */ |
744d62ee RV |
54 | { 0xc0f, "Cortex-A15" }, |
55 | { 0xc0e, "Cortex-A17" }, | |
56 | { 0xc14, "Cortex-R4" }, | |
57 | { 0xc15, "Cortex-R5" }, | |
58 | { 0xc17, "Cortex-R7" }, | |
59 | { 0xc18, "Cortex-R8" }, | |
60 | { 0xc20, "Cortex-M0" }, | |
61 | { 0xc21, "Cortex-M1" }, | |
62 | { 0xc23, "Cortex-M3" }, | |
63 | { 0xc24, "Cortex-M4" }, | |
e6601a70 | 64 | { 0xc27, "Cortex-M7" }, |
744d62ee RV |
65 | { 0xc60, "Cortex-M0+" }, |
66 | { 0xd01, "Cortex-A32" }, | |
b22a3bb5 | 67 | { 0xd02, "Cortex-A34" }, |
744d62ee RV |
68 | { 0xd03, "Cortex-A53" }, |
69 | { 0xd04, "Cortex-A35" }, | |
70 | { 0xd05, "Cortex-A55" }, | |
dbc27723 | 71 | { 0xd06, "Cortex-A65" }, |
744d62ee RV |
72 | { 0xd07, "Cortex-A57" }, |
73 | { 0xd08, "Cortex-A72" }, | |
74 | { 0xd09, "Cortex-A73" }, | |
75 | { 0xd0a, "Cortex-A75" }, | |
8d124f0f JL |
76 | { 0xd0b, "Cortex-A76" }, |
77 | { 0xd0c, "Neoverse-N1" }, | |
dbc27723 JL |
78 | { 0xd0d, "Cortex-A77" }, |
79 | { 0xd0e, "Cortex-A76AE" }, | |
744d62ee | 80 | { 0xd13, "Cortex-R52" }, |
6857cccb | 81 | { 0xd15, "Cortex-R82" }, |
6112ade9 | 82 | { 0xd16, "Cortex-R52+" }, |
744d62ee RV |
83 | { 0xd20, "Cortex-M23" }, |
84 | { 0xd21, "Cortex-M33" }, | |
6112ade9 JL |
85 | { 0xd22, "Cortex-M55" }, |
86 | { 0xd23, "Cortex-M85" }, | |
5148c84e | 87 | { 0xd40, "Neoverse-V1" }, |
dbc27723 JL |
88 | { 0xd41, "Cortex-A78" }, |
89 | { 0xd42, "Cortex-A78AE" }, | |
b22a3bb5 | 90 | { 0xd43, "Cortex-A65AE" }, |
5148c84e | 91 | { 0xd44, "Cortex-X1" }, |
8cdb31d1 T |
92 | { 0xd46, "Cortex-A510" }, |
93 | { 0xd47, "Cortex-A710" }, | |
5148c84e AS |
94 | { 0xd48, "Cortex-X2" }, |
95 | { 0xd49, "Neoverse-N2" }, | |
8d124f0f | 96 | { 0xd4a, "Neoverse-E1" }, |
dbc27723 | 97 | { 0xd4b, "Cortex-A78C" }, |
b22a3bb5 | 98 | { 0xd4c, "Cortex-X1C" }, |
8cdb31d1 T |
99 | { 0xd4d, "Cortex-A715" }, |
100 | { 0xd4e, "Cortex-X3" }, | |
6857cccb | 101 | { 0xd4f, "Neoverse-V2" }, |
6112ade9 JL |
102 | { 0xd80, "Cortex-A520" }, |
103 | { 0xd81, "Cortex-A720" }, | |
104 | { 0xd82, "Cortex-X4" }, | |
744d62ee RV |
105 | { -1, "unknown" }, |
106 | }; | |
107 | ||
108 | static const struct id_part brcm_part[] = { | |
c6df41fa KZ |
109 | { 0x0f, "Brahma-B15" }, |
110 | { 0x100, "Brahma-B53" }, | |
744d62ee RV |
111 | { 0x516, "ThunderX2" }, |
112 | { -1, "unknown" }, | |
113 | }; | |
114 | ||
115 | static const struct id_part dec_part[] = { | |
116 | { 0xa10, "SA110" }, | |
117 | { 0xa11, "SA1100" }, | |
118 | { -1, "unknown" }, | |
119 | }; | |
120 | ||
121 | static const struct id_part cavium_part[] = { | |
122 | { 0x0a0, "ThunderX" }, | |
c6df41fa KZ |
123 | { 0x0a1, "ThunderX-88XX" }, |
124 | { 0x0a2, "ThunderX-81XX" }, | |
125 | { 0x0a3, "ThunderX-83XX" }, | |
126 | { 0x0af, "ThunderX2-99xx" }, | |
c7e659f8 T |
127 | { 0x0b0, "OcteonTX2" }, |
128 | { 0x0b1, "OcteonTX2-98XX" }, | |
129 | { 0x0b2, "OcteonTX2-96XX" }, | |
130 | { 0x0b3, "OcteonTX2-95XX" }, | |
131 | { 0x0b4, "OcteonTX2-95XXN" }, | |
132 | { 0x0b5, "OcteonTX2-95XXMM" }, | |
133 | { 0x0b6, "OcteonTX2-95XXO" }, | |
01e2697a | 134 | { 0x0b8, "ThunderX3-T110" }, |
744d62ee RV |
135 | { -1, "unknown" }, |
136 | }; | |
137 | ||
138 | static const struct id_part apm_part[] = { | |
139 | { 0x000, "X-Gene" }, | |
140 | { -1, "unknown" }, | |
141 | }; | |
142 | ||
143 | static const struct id_part qcom_part[] = { | |
144 | { 0x00f, "Scorpion" }, | |
145 | { 0x02d, "Scorpion" }, | |
146 | { 0x04d, "Krait" }, | |
147 | { 0x06f, "Krait" }, | |
148 | { 0x201, "Kryo" }, | |
149 | { 0x205, "Kryo" }, | |
150 | { 0x211, "Kryo" }, | |
c6df41fa KZ |
151 | { 0x800, "Falkor-V1/Kryo" }, |
152 | { 0x801, "Kryo-V2" }, | |
7076703b | 153 | { 0x802, "Kryo-3XX-Gold" }, |
c6df41fa KZ |
154 | { 0x803, "Kryo-3XX-Silver" }, |
155 | { 0x804, "Kryo-4XX-Gold" }, | |
156 | { 0x805, "Kryo-4XX-Silver" }, | |
744d62ee RV |
157 | { 0xc00, "Falkor" }, |
158 | { 0xc01, "Saphira" }, | |
159 | { -1, "unknown" }, | |
160 | }; | |
161 | ||
162 | static const struct id_part samsung_part[] = { | |
163 | { 0x001, "exynos-m1" }, | |
cf922cbe | 164 | { 0x002, "exynos-m3" }, |
df1fc195 | 165 | { 0x003, "exynos-m4" }, |
b93a604a | 166 | { 0x004, "exynos-m5" }, |
744d62ee | 167 | { -1, "unknown" }, |
744d62ee RV |
168 | }; |
169 | ||
170 | static const struct id_part nvidia_part[] = { | |
171 | { 0x000, "Denver" }, | |
172 | { 0x003, "Denver 2" }, | |
793378df | 173 | { 0x004, "Carmel" }, |
744d62ee RV |
174 | { -1, "unknown" }, |
175 | }; | |
176 | ||
177 | static const struct id_part marvell_part[] = { | |
c6df41fa | 178 | { 0x131, "Feroceon-88FR131" }, |
744d62ee RV |
179 | { 0x581, "PJ4/PJ4b" }, |
180 | { 0x584, "PJ4B-MP" }, | |
181 | { -1, "unknown" }, | |
dd9b4cb3 | 182 | }; |
744d62ee | 183 | |
42c8ad63 | 184 | static const struct id_part apple_part[] = { |
820e61d0 T |
185 | { 0x000, "Swift" }, |
186 | { 0x001, "Cyclone" }, | |
187 | { 0x002, "Typhoon" }, | |
188 | { 0x003, "Typhoon/Capri" }, | |
189 | { 0x004, "Twister" }, | |
190 | { 0x005, "Twister/Elba/Malta" }, | |
191 | { 0x006, "Hurricane" }, | |
192 | { 0x007, "Hurricane/Myst" }, | |
193 | { 0x008, "Monsoon" }, | |
194 | { 0x009, "Mistral" }, | |
195 | { 0x00b, "Vortex" }, | |
196 | { 0x00c, "Tempest" }, | |
197 | { 0x00f, "Tempest-M9" }, | |
198 | { 0x010, "Vortex/Aruba" }, | |
199 | { 0x011, "Tempest/Aruba" }, | |
200 | { 0x012, "Lightning" }, | |
201 | { 0x013, "Thunder" }, | |
3b565d31 T |
202 | { 0x020, "Icestorm-A14" }, |
203 | { 0x021, "Firestorm-A14" }, | |
204 | { 0x022, "Icestorm-M1" }, | |
205 | { 0x023, "Firestorm-M1" }, | |
206 | { 0x024, "Icestorm-M1-Pro" }, | |
207 | { 0x025, "Firestorm-M1-Pro" }, | |
820e61d0 | 208 | { 0x026, "Thunder-M10" }, |
3b565d31 T |
209 | { 0x028, "Icestorm-M1-Max" }, |
210 | { 0x029, "Firestorm-M1-Max" }, | |
c6df41fa KZ |
211 | { 0x030, "Blizzard-A15" }, |
212 | { 0x031, "Avalanche-A15" }, | |
213 | { 0x032, "Blizzard-M2" }, | |
214 | { 0x033, "Avalanche-M2" }, | |
e97637ec T |
215 | { 0x034, "Blizzard-M2-Pro" }, |
216 | { 0x035, "Avalanche-M2-Pro" }, | |
217 | { 0x036, "Sawtooth-A16" }, | |
218 | { 0x037, "Everest-A16" }, | |
9bd0ed6f | 219 | { 0x038, "Blizzard-M2-Max" }, |
e97637ec | 220 | { 0x039, "Avalanche-M2-Max" }, |
cf15fdfd | 221 | { -1, "unknown" }, |
42c8ad63 T |
222 | }; |
223 | ||
dd9b4cb3 RV |
224 | static const struct id_part faraday_part[] = { |
225 | { 0x526, "FA526" }, | |
226 | { 0x626, "FA626" }, | |
227 | { -1, "unknown" }, | |
744d62ee RV |
228 | }; |
229 | ||
230 | static const struct id_part intel_part[] = { | |
231 | { 0x200, "i80200" }, | |
232 | { 0x210, "PXA250A" }, | |
233 | { 0x212, "PXA210A" }, | |
234 | { 0x242, "i80321-400" }, | |
235 | { 0x243, "i80321-600" }, | |
236 | { 0x290, "PXA250B/PXA26x" }, | |
237 | { 0x292, "PXA210B" }, | |
238 | { 0x2c2, "i80321-400-B0" }, | |
239 | { 0x2c3, "i80321-600-B0" }, | |
240 | { 0x2d0, "PXA250C/PXA255/PXA26x" }, | |
241 | { 0x2d2, "PXA210C" }, | |
242 | { 0x411, "PXA27x" }, | |
243 | { 0x41c, "IPX425-533" }, | |
244 | { 0x41d, "IPX425-400" }, | |
245 | { 0x41f, "IPX425-266" }, | |
246 | { 0x682, "PXA32x" }, | |
247 | { 0x683, "PXA930/PXA935" }, | |
248 | { 0x688, "PXA30x" }, | |
249 | { 0x689, "PXA31x" }, | |
250 | { 0xb11, "SA1110" }, | |
251 | { 0xc12, "IPX1200" }, | |
252 | { -1, "unknown" }, | |
253 | }; | |
254 | ||
a625b32e SN |
255 | static const struct id_part fujitsu_part[] = { |
256 | { 0x001, "A64FX" }, | |
257 | { -1, "unknown" }, | |
258 | }; | |
259 | ||
6f00af5b | 260 | static const struct id_part hisi_part[] = { |
26b17f80 TK |
261 | { 0xd01, "TaiShan-v110" }, /* used in Kunpeng-920 SoC */ |
262 | { 0xd02, "TaiShan-v120" }, /* used in Kirin 990A and 9000S SoCs */ | |
50bcc673 | 263 | { 0xd40, "Cortex-A76" }, /* HiSilicon uses this ID though advertises A76 */ |
26b17f80 | 264 | { 0xd41, "Cortex-A77" }, /* HiSilicon uses this ID though advertises A77 */ |
6f00af5b JG |
265 | { -1, "unknown" }, |
266 | }; | |
267 | ||
db81b3fc T |
268 | static const struct id_part ampere_part[] = { |
269 | { 0xac3, "Ampere-1" }, | |
270 | { 0xac4, "Ampere-1a" }, | |
271 | { -1, "unknown" }, | |
272 | }; | |
273 | ||
642a1a00 | 274 | static const struct id_part ft_part[] = { |
5ab35198 | 275 | { 0x660, "FTC660" }, |
97c61e5e | 276 | { 0x661, "FTC661" }, |
42c8ad63 T |
277 | { 0x662, "FTC662" }, |
278 | { 0x663, "FTC663" }, | |
b36a7925 | 279 | { 0x862, "FTC862" }, |
642a1a00 | 280 | { -1, "unknown" }, |
281 | }; | |
282 | ||
744d62ee RV |
283 | static const struct id_part unknown_part[] = { |
284 | { -1, "unknown" }, | |
285 | }; | |
286 | ||
287 | struct hw_impl { | |
288 | const int id; | |
289 | const struct id_part *parts; | |
290 | const char *name; | |
291 | }; | |
292 | ||
293 | static const struct hw_impl hw_implementer[] = { | |
294 | { 0x41, arm_part, "ARM" }, | |
295 | { 0x42, brcm_part, "Broadcom" }, | |
296 | { 0x43, cavium_part, "Cavium" }, | |
297 | { 0x44, dec_part, "DEC" }, | |
a625b32e | 298 | { 0x46, fujitsu_part, "FUJITSU" }, |
8243036c | 299 | { 0x48, hisi_part, "HiSilicon" }, |
d44a83c1 VS |
300 | { 0x49, unknown_part, "Infineon" }, |
301 | { 0x4d, unknown_part, "Motorola/Freescale" }, | |
ecec8f1f | 302 | { 0x4e, nvidia_part, "NVIDIA" }, |
744d62ee RV |
303 | { 0x50, apm_part, "APM" }, |
304 | { 0x51, qcom_part, "Qualcomm" }, | |
305 | { 0x53, samsung_part, "Samsung" }, | |
306 | { 0x56, marvell_part, "Marvell" }, | |
42c8ad63 | 307 | { 0x61, apple_part, "Apple" }, |
dd9b4cb3 | 308 | { 0x66, faraday_part, "Faraday" }, |
744d62ee | 309 | { 0x69, intel_part, "Intel" }, |
642a1a00 | 310 | { 0x70, ft_part, "Phytium" }, |
db81b3fc | 311 | { 0xc0, ampere_part, "Ampere" }, |
744d62ee RV |
312 | { -1, unknown_part, "unknown" }, |
313 | }; | |
314 | ||
504de585 | 315 | static int parse_id(const char *str) |
8229df20 | 316 | { |
504de585 KZ |
317 | int id; |
318 | char *end = NULL; | |
af808dfa | 319 | |
504de585 KZ |
320 | if (!str || strncmp(str, "0x",2) != 0) |
321 | return -EINVAL; | |
af808dfa | 322 | |
504de585 KZ |
323 | errno = 0; |
324 | id = (int) strtol(str, &end, 0); | |
325 | if (errno || str == end) | |
326 | return -EINVAL; | |
367c85c4 | 327 | |
504de585 KZ |
328 | return id; |
329 | } | |
367c85c4 | 330 | |
504de585 KZ |
331 | #define parse_model_id(_cxt) (parse_id((_cxt)->model)) |
332 | ||
76b6666c KZ |
333 | static inline int parse_implementer_id(struct lscpu_cputype *ct) |
334 | { | |
335 | if (ct->vendor_id) | |
336 | return ct->vendor_id; | |
337 | ct->vendor_id = parse_id(ct->vendor); | |
338 | return ct->vendor_id; | |
339 | } | |
340 | ||
504de585 KZ |
341 | /* |
342 | * Use model and vendor IDs to decode to human readable names. | |
343 | */ | |
344 | static int arm_ids_decode(struct lscpu_cputype *ct) | |
345 | { | |
346 | int impl, part, j; | |
347 | const struct id_part *parts = NULL; | |
367c85c4 | 348 | |
504de585 KZ |
349 | impl = parse_implementer_id(ct); |
350 | if (impl <= 0) | |
351 | return -EINVAL; /* no ARM or missing ID */ | |
352 | ||
353 | /* decode vendor */ | |
354 | for (j = 0; hw_implementer[j].id != -1; j++) { | |
355 | if (hw_implementer[j].id == impl) { | |
356 | parts = hw_implementer[j].parts; | |
357 | free(ct->vendor); | |
358 | ct->vendor = xstrdup(hw_implementer[j].name); | |
359 | break; | |
367c85c4 | 360 | } |
504de585 KZ |
361 | } |
362 | ||
363 | /* decode model */ | |
364 | if (!parts) | |
365 | goto done; | |
367c85c4 | 366 | |
504de585 KZ |
367 | part = parse_model_id(ct); |
368 | if (part <= 0) | |
369 | goto done; | |
8229df20 | 370 | |
504de585 KZ |
371 | for (j = 0; parts[j].id != -1; j++) { |
372 | if (parts[j].id == part) { | |
373 | free(ct->modelname); | |
374 | ct->modelname = xstrdup(parts[j].name); | |
375 | break; | |
8229df20 KZ |
376 | } |
377 | } | |
504de585 KZ |
378 | done: |
379 | return 0; | |
380 | } | |
8229df20 | 381 | |
504de585 | 382 | /* use "rXpY" string as stepping */ |
8014104b | 383 | static int arm_rXpY_decode(struct lscpu_cputype *ct) |
504de585 KZ |
384 | { |
385 | int impl, revision, variant; | |
386 | char *end = NULL; | |
387 | char buf[8]; | |
af808dfa | 388 | |
504de585 | 389 | impl = parse_implementer_id(ct); |
af808dfa | 390 | |
504de585 KZ |
391 | if (impl != 0x41 || !ct->revision || !ct->stepping) |
392 | return -EINVAL; | |
af808dfa | 393 | |
504de585 KZ |
394 | errno = 0; |
395 | revision = (int) strtol(ct->revision, &end, 10); | |
396 | if (errno || ct->revision == end) | |
397 | return -EINVAL; | |
398 | ||
399 | errno = 0; | |
400 | variant = (int) strtol(ct->stepping, &end, 0); | |
401 | if (errno || ct->stepping == end) | |
402 | return -EINVAL; | |
403 | ||
404 | snprintf(buf, sizeof(buf), "r%dp%d", variant, revision); | |
405 | free(ct->stepping); | |
406 | ct->stepping = xstrdup(buf); | |
407 | ||
408 | return 0; | |
8229df20 | 409 | } |
367c85c4 | 410 | |
504de585 | 411 | static void arm_decode(struct lscpu_cxt *cxt, struct lscpu_cputype *ct) |
367c85c4 | 412 | { |
81d6de91 | 413 | if (!cxt->noalive && access(_PATH_SYS_DMI, R_OK) == 0) |
a772d7c4 | 414 | dmi_decode_cputype(ct); |
504de585 | 415 | |
8014104b MM |
416 | arm_ids_decode(ct); |
417 | arm_rXpY_decode(ct); | |
c2ca2837 KZ |
418 | |
419 | if (!cxt->noalive && cxt->is_cluster) | |
f42f105b | 420 | ct->nr_socket_on_cluster = get_number_of_physical_sockets_from_dmi(); |
504de585 KZ |
421 | } |
422 | ||
5ebff091 | 423 | static int is_cluster_arm(struct lscpu_cxt *cxt) |
73c0a766 MM |
424 | { |
425 | struct stat st; | |
426 | ||
c2ca2837 KZ |
427 | if (!cxt->noalive |
428 | && strcmp(cxt->arch->name, "aarch64") == 0 | |
429 | && stat(_PATH_ACPI_PPTT, &st) < 0 && cxt->ncputypes == 1) | |
73c0a766 MM |
430 | return 1; |
431 | else | |
432 | return 0; | |
433 | } | |
434 | ||
504de585 KZ |
435 | void lscpu_decode_arm(struct lscpu_cxt *cxt) |
436 | { | |
437 | size_t i; | |
438 | ||
5ebff091 | 439 | cxt->is_cluster = is_cluster_arm(cxt); |
73c0a766 | 440 | |
504de585 KZ |
441 | for (i = 0; i < cxt->ncputypes; i++) |
442 | arm_decode(cxt, cxt->cputypes[i]); | |
367c85c4 | 443 | } |