1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Record and handle CPU attributes. |
4 | * |
5 | * Copyright (C) 2014 ARM Ltd. |
6 | */ |
7 | #include <asm/arch_timer.h> |
8 | #include <asm/cache.h> |
9 | #include <asm/cpu.h> |
10 | #include <asm/cputype.h> |
11 | #include <asm/cpufeature.h> |
12 | #include <asm/fpsimd.h> |
13 | |
14 | #include <linux/bitops.h> |
15 | #include <linux/bug.h> |
16 | #include <linux/compat.h> |
17 | #include <linux/elf.h> |
18 | #include <linux/init.h> |
19 | #include <linux/kernel.h> |
20 | #include <linux/personality.h> |
21 | #include <linux/preempt.h> |
22 | #include <linux/printk.h> |
23 | #include <linux/seq_file.h> |
24 | #include <linux/sched.h> |
25 | #include <linux/smp.h> |
26 | #include <linux/delay.h> |
27 | |
28 | /* |
29 | * In case the boot CPU is hotpluggable, we record its initial state and |
30 | * current state separately. Certain system registers may contain different |
31 | * values depending on configuration at or after reset. |
32 | */ |
33 | DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data); |
34 | static struct cpuinfo_arm64 boot_cpu_data; |
35 | |
36 | static inline const char *icache_policy_str(int l1ip) |
37 | { |
38 | switch (l1ip) { |
39 | case CTR_EL0_L1Ip_VIPT: |
40 | return "VIPT" ; |
41 | case CTR_EL0_L1Ip_PIPT: |
42 | return "PIPT" ; |
43 | default: |
44 | return "RESERVED/UNKNOWN" ; |
45 | } |
46 | } |
47 | |
48 | unsigned long __icache_flags; |
49 | |
50 | static const char *const hwcap_str[] = { |
51 | [KERNEL_HWCAP_FP] = "fp" , |
52 | [KERNEL_HWCAP_ASIMD] = "asimd" , |
53 | [KERNEL_HWCAP_EVTSTRM] = "evtstrm" , |
54 | [KERNEL_HWCAP_AES] = "aes" , |
55 | [KERNEL_HWCAP_PMULL] = "pmull" , |
56 | [KERNEL_HWCAP_SHA1] = "sha1" , |
57 | [KERNEL_HWCAP_SHA2] = "sha2" , |
58 | [KERNEL_HWCAP_CRC32] = "crc32" , |
59 | [KERNEL_HWCAP_ATOMICS] = "atomics" , |
60 | [KERNEL_HWCAP_FPHP] = "fphp" , |
61 | [KERNEL_HWCAP_ASIMDHP] = "asimdhp" , |
62 | [KERNEL_HWCAP_CPUID] = "cpuid" , |
63 | [KERNEL_HWCAP_ASIMDRDM] = "asimdrdm" , |
64 | [KERNEL_HWCAP_JSCVT] = "jscvt" , |
65 | [KERNEL_HWCAP_FCMA] = "fcma" , |
66 | [KERNEL_HWCAP_LRCPC] = "lrcpc" , |
67 | [KERNEL_HWCAP_DCPOP] = "dcpop" , |
68 | [KERNEL_HWCAP_SHA3] = "sha3" , |
69 | [KERNEL_HWCAP_SM3] = "sm3" , |
70 | [KERNEL_HWCAP_SM4] = "sm4" , |
71 | [KERNEL_HWCAP_ASIMDDP] = "asimddp" , |
72 | [KERNEL_HWCAP_SHA512] = "sha512" , |
73 | [KERNEL_HWCAP_SVE] = "sve" , |
74 | [KERNEL_HWCAP_ASIMDFHM] = "asimdfhm" , |
75 | [KERNEL_HWCAP_DIT] = "dit" , |
76 | [KERNEL_HWCAP_USCAT] = "uscat" , |
77 | [KERNEL_HWCAP_ILRCPC] = "ilrcpc" , |
78 | [KERNEL_HWCAP_FLAGM] = "flagm" , |
79 | [KERNEL_HWCAP_SSBS] = "ssbs" , |
80 | [KERNEL_HWCAP_SB] = "sb" , |
81 | [KERNEL_HWCAP_PACA] = "paca" , |
82 | [KERNEL_HWCAP_PACG] = "pacg" , |
83 | [KERNEL_HWCAP_DCPODP] = "dcpodp" , |
84 | [KERNEL_HWCAP_SVE2] = "sve2" , |
85 | [KERNEL_HWCAP_SVEAES] = "sveaes" , |
86 | [KERNEL_HWCAP_SVEPMULL] = "svepmull" , |
87 | [KERNEL_HWCAP_SVEBITPERM] = "svebitperm" , |
88 | [KERNEL_HWCAP_SVESHA3] = "svesha3" , |
89 | [KERNEL_HWCAP_SVESM4] = "svesm4" , |
90 | [KERNEL_HWCAP_FLAGM2] = "flagm2" , |
91 | [KERNEL_HWCAP_FRINT] = "frint" , |
92 | [KERNEL_HWCAP_SVEI8MM] = "svei8mm" , |
93 | [KERNEL_HWCAP_SVEF32MM] = "svef32mm" , |
94 | [KERNEL_HWCAP_SVEF64MM] = "svef64mm" , |
95 | [KERNEL_HWCAP_SVEBF16] = "svebf16" , |
96 | [KERNEL_HWCAP_I8MM] = "i8mm" , |
97 | [KERNEL_HWCAP_BF16] = "bf16" , |
98 | [KERNEL_HWCAP_DGH] = "dgh" , |
99 | [KERNEL_HWCAP_RNG] = "rng" , |
100 | [KERNEL_HWCAP_BTI] = "bti" , |
101 | [KERNEL_HWCAP_MTE] = "mte" , |
102 | [KERNEL_HWCAP_ECV] = "ecv" , |
103 | [KERNEL_HWCAP_AFP] = "afp" , |
104 | [KERNEL_HWCAP_RPRES] = "rpres" , |
105 | [KERNEL_HWCAP_MTE3] = "mte3" , |
106 | [KERNEL_HWCAP_SME] = "sme" , |
107 | [KERNEL_HWCAP_SME_I16I64] = "smei16i64" , |
108 | [KERNEL_HWCAP_SME_F64F64] = "smef64f64" , |
109 | [KERNEL_HWCAP_SME_I8I32] = "smei8i32" , |
110 | [KERNEL_HWCAP_SME_F16F32] = "smef16f32" , |
111 | [KERNEL_HWCAP_SME_B16F32] = "smeb16f32" , |
112 | [KERNEL_HWCAP_SME_F32F32] = "smef32f32" , |
113 | [KERNEL_HWCAP_SME_FA64] = "smefa64" , |
114 | [KERNEL_HWCAP_WFXT] = "wfxt" , |
115 | [KERNEL_HWCAP_EBF16] = "ebf16" , |
116 | [KERNEL_HWCAP_SVE_EBF16] = "sveebf16" , |
117 | [KERNEL_HWCAP_CSSC] = "cssc" , |
118 | [KERNEL_HWCAP_RPRFM] = "rprfm" , |
119 | [KERNEL_HWCAP_SVE2P1] = "sve2p1" , |
120 | [KERNEL_HWCAP_SME2] = "sme2" , |
121 | [KERNEL_HWCAP_SME2P1] = "sme2p1" , |
122 | [KERNEL_HWCAP_SME_I16I32] = "smei16i32" , |
123 | [KERNEL_HWCAP_SME_BI32I32] = "smebi32i32" , |
124 | [KERNEL_HWCAP_SME_B16B16] = "smeb16b16" , |
125 | [KERNEL_HWCAP_SME_F16F16] = "smef16f16" , |
126 | [KERNEL_HWCAP_MOPS] = "mops" , |
127 | [KERNEL_HWCAP_HBC] = "hbc" , |
128 | [KERNEL_HWCAP_SVE_B16B16] = "sveb16b16" , |
129 | [KERNEL_HWCAP_LRCPC3] = "lrcpc3" , |
130 | [KERNEL_HWCAP_LSE128] = "lse128" , |
131 | [KERNEL_HWCAP_FPMR] = "fpmr" , |
132 | [KERNEL_HWCAP_LUT] = "lut" , |
133 | [KERNEL_HWCAP_FAMINMAX] = "faminmax" , |
134 | [KERNEL_HWCAP_F8CVT] = "f8cvt" , |
135 | [KERNEL_HWCAP_F8FMA] = "f8fma" , |
136 | [KERNEL_HWCAP_F8DP4] = "f8dp4" , |
137 | [KERNEL_HWCAP_F8DP2] = "f8dp2" , |
138 | [KERNEL_HWCAP_F8E4M3] = "f8e4m3" , |
139 | [KERNEL_HWCAP_F8E5M2] = "f8e5m2" , |
140 | [KERNEL_HWCAP_SME_LUTV2] = "smelutv2" , |
141 | [KERNEL_HWCAP_SME_F8F16] = "smef8f16" , |
142 | [KERNEL_HWCAP_SME_F8F32] = "smef8f32" , |
143 | [KERNEL_HWCAP_SME_SF8FMA] = "smesf8fma" , |
144 | [KERNEL_HWCAP_SME_SF8DP4] = "smesf8dp4" , |
145 | [KERNEL_HWCAP_SME_SF8DP2] = "smesf8dp2" , |
146 | }; |
147 | |
148 | #ifdef CONFIG_COMPAT |
149 | #define COMPAT_KERNEL_HWCAP(x) const_ilog2(COMPAT_HWCAP_ ## x) |
150 | static const char *const compat_hwcap_str[] = { |
151 | [COMPAT_KERNEL_HWCAP(SWP)] = "swp" , |
152 | [COMPAT_KERNEL_HWCAP(HALF)] = "half" , |
153 | [COMPAT_KERNEL_HWCAP(THUMB)] = "thumb" , |
154 | [COMPAT_KERNEL_HWCAP(26BIT)] = NULL, /* Not possible on arm64 */ |
155 | [COMPAT_KERNEL_HWCAP(FAST_MULT)] = "fastmult" , |
156 | [COMPAT_KERNEL_HWCAP(FPA)] = NULL, /* Not possible on arm64 */ |
157 | [COMPAT_KERNEL_HWCAP(VFP)] = "vfp" , |
158 | [COMPAT_KERNEL_HWCAP(EDSP)] = "edsp" , |
159 | [COMPAT_KERNEL_HWCAP(JAVA)] = NULL, /* Not possible on arm64 */ |
160 | [COMPAT_KERNEL_HWCAP(IWMMXT)] = NULL, /* Not possible on arm64 */ |
161 | [COMPAT_KERNEL_HWCAP(CRUNCH)] = NULL, /* Not possible on arm64 */ |
162 | [COMPAT_KERNEL_HWCAP(THUMBEE)] = NULL, /* Not possible on arm64 */ |
163 | [COMPAT_KERNEL_HWCAP(NEON)] = "neon" , |
164 | [COMPAT_KERNEL_HWCAP(VFPv3)] = "vfpv3" , |
165 | [COMPAT_KERNEL_HWCAP(VFPV3D16)] = NULL, /* Not possible on arm64 */ |
166 | [COMPAT_KERNEL_HWCAP(TLS)] = "tls" , |
167 | [COMPAT_KERNEL_HWCAP(VFPv4)] = "vfpv4" , |
168 | [COMPAT_KERNEL_HWCAP(IDIVA)] = "idiva" , |
169 | [COMPAT_KERNEL_HWCAP(IDIVT)] = "idivt" , |
170 | [COMPAT_KERNEL_HWCAP(VFPD32)] = NULL, /* Not possible on arm64 */ |
171 | [COMPAT_KERNEL_HWCAP(LPAE)] = "lpae" , |
172 | [COMPAT_KERNEL_HWCAP(EVTSTRM)] = "evtstrm" , |
173 | [COMPAT_KERNEL_HWCAP(FPHP)] = "fphp" , |
174 | [COMPAT_KERNEL_HWCAP(ASIMDHP)] = "asimdhp" , |
175 | [COMPAT_KERNEL_HWCAP(ASIMDDP)] = "asimddp" , |
176 | [COMPAT_KERNEL_HWCAP(ASIMDFHM)] = "asimdfhm" , |
177 | [COMPAT_KERNEL_HWCAP(ASIMDBF16)] = "asimdbf16" , |
178 | [COMPAT_KERNEL_HWCAP(I8MM)] = "i8mm" , |
179 | }; |
180 | |
181 | #define COMPAT_KERNEL_HWCAP2(x) const_ilog2(COMPAT_HWCAP2_ ## x) |
182 | static const char *const compat_hwcap2_str[] = { |
183 | [COMPAT_KERNEL_HWCAP2(AES)] = "aes" , |
184 | [COMPAT_KERNEL_HWCAP2(PMULL)] = "pmull" , |
185 | [COMPAT_KERNEL_HWCAP2(SHA1)] = "sha1" , |
186 | [COMPAT_KERNEL_HWCAP2(SHA2)] = "sha2" , |
187 | [COMPAT_KERNEL_HWCAP2(CRC32)] = "crc32" , |
188 | [COMPAT_KERNEL_HWCAP2(SB)] = "sb" , |
189 | [COMPAT_KERNEL_HWCAP2(SSBS)] = "ssbs" , |
190 | }; |
191 | #endif /* CONFIG_COMPAT */ |
192 | |
193 | static int c_show(struct seq_file *m, void *v) |
194 | { |
195 | int i, j; |
196 | bool compat = personality(current->personality) == PER_LINUX32; |
197 | |
198 | for_each_online_cpu(i) { |
199 | struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i); |
200 | u32 midr = cpuinfo->reg_midr; |
201 | |
202 | /* |
203 | * glibc reads /proc/cpuinfo to determine the number of |
204 | * online processors, looking for lines beginning with |
205 | * "processor". Give glibc what it expects. |
206 | */ |
207 | seq_printf(m, fmt: "processor\t: %d\n" , i); |
208 | if (compat) |
209 | seq_printf(m, fmt: "model name\t: ARMv8 Processor rev %d (%s)\n" , |
210 | MIDR_REVISION(midr), COMPAT_ELF_PLATFORM); |
211 | |
212 | seq_printf(m, fmt: "BogoMIPS\t: %lu.%02lu\n" , |
213 | loops_per_jiffy / (500000UL/HZ), |
214 | loops_per_jiffy / (5000UL/HZ) % 100); |
215 | |
216 | /* |
217 | * Dump out the common processor features in a single line. |
218 | * Userspace should read the hwcaps with getauxval(AT_HWCAP) |
219 | * rather than attempting to parse this, but there's a body of |
220 | * software which does already (at least for 32-bit). |
221 | */ |
222 | seq_puts(m, s: "Features\t:" ); |
223 | if (compat) { |
224 | #ifdef CONFIG_COMPAT |
225 | for (j = 0; j < ARRAY_SIZE(compat_hwcap_str); j++) { |
226 | if (compat_elf_hwcap & (1 << j)) { |
227 | /* |
228 | * Warn once if any feature should not |
229 | * have been present on arm64 platform. |
230 | */ |
231 | if (WARN_ON_ONCE(!compat_hwcap_str[j])) |
232 | continue; |
233 | |
234 | seq_printf(m, " %s" , compat_hwcap_str[j]); |
235 | } |
236 | } |
237 | |
238 | for (j = 0; j < ARRAY_SIZE(compat_hwcap2_str); j++) |
239 | if (compat_elf_hwcap2 & (1 << j)) |
240 | seq_printf(m, " %s" , compat_hwcap2_str[j]); |
241 | #endif /* CONFIG_COMPAT */ |
242 | } else { |
243 | for (j = 0; j < ARRAY_SIZE(hwcap_str); j++) |
244 | if (cpu_have_feature(j)) |
245 | seq_printf(m, " %s" , hwcap_str[j]); |
246 | } |
247 | seq_puts(m, s: "\n" ); |
248 | |
249 | seq_printf(m, fmt: "CPU implementer\t: 0x%02x\n" , |
250 | MIDR_IMPLEMENTOR(midr)); |
251 | seq_printf(m, fmt: "CPU architecture: 8\n" ); |
252 | seq_printf(m, fmt: "CPU variant\t: 0x%x\n" , MIDR_VARIANT(midr)); |
253 | seq_printf(m, fmt: "CPU part\t: 0x%03x\n" , MIDR_PARTNUM(midr)); |
254 | seq_printf(m, fmt: "CPU revision\t: %d\n\n" , MIDR_REVISION(midr)); |
255 | } |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | static void *c_start(struct seq_file *m, loff_t *pos) |
261 | { |
262 | return *pos < 1 ? (void *)1 : NULL; |
263 | } |
264 | |
265 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) |
266 | { |
267 | ++*pos; |
268 | return NULL; |
269 | } |
270 | |
271 | static void c_stop(struct seq_file *m, void *v) |
272 | { |
273 | } |
274 | |
275 | const struct seq_operations cpuinfo_op = { |
276 | .start = c_start, |
277 | .next = c_next, |
278 | .stop = c_stop, |
279 | .show = c_show |
280 | }; |
281 | |
282 | |
283 | static struct kobj_type cpuregs_kobj_type = { |
284 | .sysfs_ops = &kobj_sysfs_ops, |
285 | }; |
286 | |
287 | /* |
288 | * The ARM ARM uses the phrase "32-bit register" to describe a register |
289 | * whose upper 32 bits are RES0 (per C5.1.1, ARM DDI 0487A.i), however |
290 | * no statement is made as to whether the upper 32 bits will or will not |
291 | * be made use of in future, and between ARM DDI 0487A.c and ARM DDI |
292 | * 0487A.d CLIDR_EL1 was expanded from 32-bit to 64-bit. |
293 | * |
294 | * Thus, while both MIDR_EL1 and REVIDR_EL1 are described as 32-bit |
295 | * registers, we expose them both as 64 bit values to cater for possible |
296 | * future expansion without an ABI break. |
297 | */ |
298 | #define kobj_to_cpuinfo(kobj) container_of(kobj, struct cpuinfo_arm64, kobj) |
299 | #define CPUREGS_ATTR_RO(_name, _field) \ |
300 | static ssize_t _name##_show(struct kobject *kobj, \ |
301 | struct kobj_attribute *attr, char *buf) \ |
302 | { \ |
303 | struct cpuinfo_arm64 *info = kobj_to_cpuinfo(kobj); \ |
304 | \ |
305 | if (info->reg_midr) \ |
306 | return sprintf(buf, "0x%016llx\n", info->reg_##_field); \ |
307 | else \ |
308 | return 0; \ |
309 | } \ |
310 | static struct kobj_attribute cpuregs_attr_##_name = __ATTR_RO(_name) |
311 | |
312 | CPUREGS_ATTR_RO(midr_el1, midr); |
313 | CPUREGS_ATTR_RO(revidr_el1, revidr); |
314 | CPUREGS_ATTR_RO(smidr_el1, smidr); |
315 | |
316 | static struct attribute *cpuregs_id_attrs[] = { |
317 | &cpuregs_attr_midr_el1.attr, |
318 | &cpuregs_attr_revidr_el1.attr, |
319 | NULL |
320 | }; |
321 | |
322 | static const struct attribute_group cpuregs_attr_group = { |
323 | .attrs = cpuregs_id_attrs, |
324 | .name = "identification" |
325 | }; |
326 | |
327 | static struct attribute *sme_cpuregs_id_attrs[] = { |
328 | &cpuregs_attr_smidr_el1.attr, |
329 | NULL |
330 | }; |
331 | |
332 | static const struct attribute_group sme_cpuregs_attr_group = { |
333 | .attrs = sme_cpuregs_id_attrs, |
334 | .name = "identification" |
335 | }; |
336 | |
337 | static int cpuid_cpu_online(unsigned int cpu) |
338 | { |
339 | int rc; |
340 | struct device *dev; |
341 | struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu); |
342 | |
343 | dev = get_cpu_device(cpu); |
344 | if (!dev) { |
345 | rc = -ENODEV; |
346 | goto out; |
347 | } |
348 | rc = kobject_add(kobj: &info->kobj, parent: &dev->kobj, fmt: "regs" ); |
349 | if (rc) |
350 | goto out; |
351 | rc = sysfs_create_group(kobj: &info->kobj, grp: &cpuregs_attr_group); |
352 | if (rc) |
353 | kobject_del(kobj: &info->kobj); |
354 | if (system_supports_sme()) |
355 | rc = sysfs_merge_group(kobj: &info->kobj, grp: &sme_cpuregs_attr_group); |
356 | out: |
357 | return rc; |
358 | } |
359 | |
360 | static int cpuid_cpu_offline(unsigned int cpu) |
361 | { |
362 | struct device *dev; |
363 | struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu); |
364 | |
365 | dev = get_cpu_device(cpu); |
366 | if (!dev) |
367 | return -ENODEV; |
368 | if (info->kobj.parent) { |
369 | sysfs_remove_group(kobj: &info->kobj, grp: &cpuregs_attr_group); |
370 | kobject_del(kobj: &info->kobj); |
371 | } |
372 | |
373 | return 0; |
374 | } |
375 | |
376 | static int __init cpuinfo_regs_init(void) |
377 | { |
378 | int cpu, ret; |
379 | |
380 | for_each_possible_cpu(cpu) { |
381 | struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu); |
382 | |
383 | kobject_init(kobj: &info->kobj, ktype: &cpuregs_kobj_type); |
384 | } |
385 | |
386 | ret = cpuhp_setup_state(state: CPUHP_AP_ONLINE_DYN, name: "arm64/cpuinfo:online" , |
387 | startup: cpuid_cpu_online, teardown: cpuid_cpu_offline); |
388 | if (ret < 0) { |
389 | pr_err("cpuinfo: failed to register hotplug callbacks.\n" ); |
390 | return ret; |
391 | } |
392 | return 0; |
393 | } |
394 | device_initcall(cpuinfo_regs_init); |
395 | |
396 | static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info) |
397 | { |
398 | unsigned int cpu = smp_processor_id(); |
399 | u32 l1ip = CTR_L1IP(info->reg_ctr); |
400 | |
401 | switch (l1ip) { |
402 | case CTR_EL0_L1Ip_PIPT: |
403 | break; |
404 | case CTR_EL0_L1Ip_VIPT: |
405 | default: |
406 | /* Assume aliasing */ |
407 | set_bit(ICACHEF_ALIASING, &__icache_flags); |
408 | break; |
409 | } |
410 | |
411 | pr_info("Detected %s I-cache on CPU%d\n" , icache_policy_str(l1ip), cpu); |
412 | } |
413 | |
414 | static void __cpuinfo_store_cpu_32bit(struct cpuinfo_32bit *info) |
415 | { |
416 | info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1); |
417 | info->reg_id_dfr1 = read_cpuid(ID_DFR1_EL1); |
418 | info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1); |
419 | info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1); |
420 | info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1); |
421 | info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1); |
422 | info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1); |
423 | info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1); |
424 | info->reg_id_isar6 = read_cpuid(ID_ISAR6_EL1); |
425 | info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1); |
426 | info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1); |
427 | info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1); |
428 | info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1); |
429 | info->reg_id_mmfr4 = read_cpuid(ID_MMFR4_EL1); |
430 | info->reg_id_mmfr5 = read_cpuid(ID_MMFR5_EL1); |
431 | info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1); |
432 | info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1); |
433 | info->reg_id_pfr2 = read_cpuid(ID_PFR2_EL1); |
434 | |
435 | info->reg_mvfr0 = read_cpuid(MVFR0_EL1); |
436 | info->reg_mvfr1 = read_cpuid(MVFR1_EL1); |
437 | info->reg_mvfr2 = read_cpuid(MVFR2_EL1); |
438 | } |
439 | |
440 | static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) |
441 | { |
442 | info->reg_cntfrq = arch_timer_get_cntfrq(); |
443 | /* |
444 | * Use the effective value of the CTR_EL0 than the raw value |
445 | * exposed by the CPU. CTR_EL0.IDC field value must be interpreted |
446 | * with the CLIDR_EL1 fields to avoid triggering false warnings |
447 | * when there is a mismatch across the CPUs. Keep track of the |
448 | * effective value of the CTR_EL0 in our internal records for |
449 | * accurate sanity check and feature enablement. |
450 | */ |
451 | info->reg_ctr = read_cpuid_effective_cachetype(); |
452 | info->reg_dczid = read_cpuid(DCZID_EL0); |
453 | info->reg_midr = read_cpuid_id(); |
454 | info->reg_revidr = read_cpuid(REVIDR_EL1); |
455 | |
456 | info->reg_id_aa64dfr0 = read_cpuid(ID_AA64DFR0_EL1); |
457 | info->reg_id_aa64dfr1 = read_cpuid(ID_AA64DFR1_EL1); |
458 | info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1); |
459 | info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1); |
460 | info->reg_id_aa64isar2 = read_cpuid(ID_AA64ISAR2_EL1); |
461 | info->reg_id_aa64isar3 = read_cpuid(ID_AA64ISAR3_EL1); |
462 | info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1); |
463 | info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1); |
464 | info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1); |
465 | info->reg_id_aa64mmfr3 = read_cpuid(ID_AA64MMFR3_EL1); |
466 | info->reg_id_aa64mmfr4 = read_cpuid(ID_AA64MMFR4_EL1); |
467 | info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1); |
468 | info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1); |
469 | info->reg_id_aa64pfr2 = read_cpuid(ID_AA64PFR2_EL1); |
470 | info->reg_id_aa64zfr0 = read_cpuid(ID_AA64ZFR0_EL1); |
471 | info->reg_id_aa64smfr0 = read_cpuid(ID_AA64SMFR0_EL1); |
472 | info->reg_id_aa64fpfr0 = read_cpuid(ID_AA64FPFR0_EL1); |
473 | |
474 | if (id_aa64pfr1_mte(info->reg_id_aa64pfr1)) |
475 | info->reg_gmid = read_cpuid(GMID_EL1); |
476 | |
477 | if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) |
478 | __cpuinfo_store_cpu_32bit(info: &info->aarch32); |
479 | |
480 | cpuinfo_detect_icache_policy(info); |
481 | } |
482 | |
483 | void cpuinfo_store_cpu(void) |
484 | { |
485 | struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data); |
486 | __cpuinfo_store_cpu(info); |
487 | update_cpu_features(smp_processor_id(), info, &boot_cpu_data); |
488 | } |
489 | |
490 | void __init cpuinfo_store_boot_cpu(void) |
491 | { |
492 | struct cpuinfo_arm64 *info = &per_cpu(cpu_data, 0); |
493 | __cpuinfo_store_cpu(info); |
494 | |
495 | boot_cpu_data = *info; |
496 | init_cpu_features(&boot_cpu_data); |
497 | } |
498 | |