blob: 1ff1c5a670818f3c5b1f9488577d22088601dfbe [file] [log] [blame]
Mark Rutlanddf857412014-07-16 16:32:44 +01001/*
2 * Record and handle CPU attributes.
3 *
4 * Copyright (C) 2014 ARM Ltd.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17#include <asm/arch_timer.h>
Will Deacon02f77602017-03-10 20:32:23 +000018#include <asm/cache.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010019#include <asm/cpu.h>
20#include <asm/cputype.h>
Andre Przywarae116a372014-11-14 15:54:09 +000021#include <asm/cpufeature.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010022
Mark Rutland59ccc0d2014-07-16 16:32:45 +010023#include <linux/bitops.h>
Ard Biesheuvel80c517b2014-08-08 12:51:39 +010024#include <linux/bug.h>
Catalin Marinase47b0202016-05-31 15:55:03 +010025#include <linux/compat.h>
26#include <linux/elf.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010027#include <linux/init.h>
Mark Rutland127161a2014-07-16 16:32:46 +010028#include <linux/kernel.h>
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010029#include <linux/personality.h>
Ard Biesheuvel80c517b2014-08-08 12:51:39 +010030#include <linux/preempt.h>
Mark Rutland59ccc0d2014-07-16 16:32:45 +010031#include <linux/printk.h>
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010032#include <linux/seq_file.h>
33#include <linux/sched.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010034#include <linux/smp.h>
Yang Shi92e788b2015-11-18 10:48:55 -080035#include <linux/delay.h>
Mark Rutlanddf857412014-07-16 16:32:44 +010036
37/*
38 * In case the boot CPU is hotpluggable, we record its initial state and
39 * current state separately. Certain system registers may contain different
40 * values depending on configuration at or after reset.
41 */
42DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data);
43static struct cpuinfo_arm64 boot_cpu_data;
44
Mark Rutland59ccc0d2014-07-16 16:32:45 +010045static char *icache_policy_str[] = {
Will Deacon155433c2017-03-10 20:32:22 +000046 [0 ... ICACHE_POLICY_PIPT] = "RESERVED/UNKNOWN",
47 [ICACHE_POLICY_VIPT] = "VIPT",
48 [ICACHE_POLICY_PIPT] = "PIPT",
Will Deacondda288d2017-03-10 20:32:24 +000049 [ICACHE_POLICY_VPIPT] = "VPIPT",
Mark Rutland59ccc0d2014-07-16 16:32:45 +010050};
51
52unsigned long __icache_flags;
53
Dave Martin9299b242015-07-30 16:36:25 +010054static const char *const hwcap_str[] = {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010055 "fp",
56 "asimd",
57 "evtstrm",
58 "aes",
59 "pmull",
60 "sha1",
61 "sha2",
62 "crc32",
63 "atomics",
Suzuki K Poulosebf500612016-01-26 15:52:46 +000064 "fphp",
65 "asimdhp",
Suzuki K Poulose77c97b42017-01-09 17:28:31 +000066 "cpuid",
Suzuki K Poulosef92f5ce02017-01-12 16:37:28 +000067 "asimdrdm",
Suzuki K Poulosec8c37982017-03-14 18:13:25 +000068 "jscvt",
Suzuki K Poulosecb567e72017-03-14 18:13:26 +000069 "fcma",
Suzuki K Poulosec651aae2017-03-14 18:13:27 +000070 "lrcpc",
Robin Murphy7aac4052017-07-25 11:55:40 +010071 "dcpop",
Suzuki K Poulosef5e035f2017-10-11 14:01:02 +010072 "sha3",
73 "sm3",
74 "sm4",
75 "asimddp",
76 "sha512",
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010077 NULL
78};
79
80#ifdef CONFIG_COMPAT
Dave Martin9299b242015-07-30 16:36:25 +010081static const char *const compat_hwcap_str[] = {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +010082 "swp",
83 "half",
84 "thumb",
85 "26bit",
86 "fastmult",
87 "fpa",
88 "vfp",
89 "edsp",
90 "java",
91 "iwmmxt",
92 "crunch",
93 "thumbee",
94 "neon",
95 "vfpv3",
96 "vfpv3d16",
97 "tls",
98 "vfpv4",
99 "idiva",
100 "idivt",
101 "vfpd32",
102 "lpae",
Julien Grallf228b492016-05-10 15:40:31 +0100103 "evtstrm",
104 NULL
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100105};
106
Dave Martin9299b242015-07-30 16:36:25 +0100107static const char *const compat_hwcap2_str[] = {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100108 "aes",
109 "pmull",
110 "sha1",
111 "sha2",
112 "crc32",
113 NULL
114};
115#endif /* CONFIG_COMPAT */
116
117static int c_show(struct seq_file *m, void *v)
118{
119 int i, j;
Catalin Marinase47b0202016-05-31 15:55:03 +0100120 bool compat = personality(current->personality) == PER_LINUX32;
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100121
122 for_each_online_cpu(i) {
123 struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
124 u32 midr = cpuinfo->reg_midr;
125
126 /*
127 * glibc reads /proc/cpuinfo to determine the number of
128 * online processors, looking for lines beginning with
129 * "processor". Give glibc what it expects.
130 */
131 seq_printf(m, "processor\t: %d\n", i);
Catalin Marinase47b0202016-05-31 15:55:03 +0100132 if (compat)
133 seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n",
134 MIDR_REVISION(midr), COMPAT_ELF_PLATFORM);
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100135
Yang Shi92e788b2015-11-18 10:48:55 -0800136 seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
137 loops_per_jiffy / (500000UL/HZ),
138 loops_per_jiffy / (5000UL/HZ) % 100);
139
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100140 /*
141 * Dump out the common processor features in a single line.
142 * Userspace should read the hwcaps with getauxval(AT_HWCAP)
143 * rather than attempting to parse this, but there's a body of
144 * software which does already (at least for 32-bit).
145 */
146 seq_puts(m, "Features\t:");
Catalin Marinase47b0202016-05-31 15:55:03 +0100147 if (compat) {
Suzuki K. Poulose12d11812015-10-19 14:24:43 +0100148#ifdef CONFIG_COMPAT
149 for (j = 0; compat_hwcap_str[j]; j++)
150 if (compat_elf_hwcap & (1 << j))
151 seq_printf(m, " %s", compat_hwcap_str[j]);
152
153 for (j = 0; compat_hwcap2_str[j]; j++)
154 if (compat_elf_hwcap2 & (1 << j))
155 seq_printf(m, " %s", compat_hwcap2_str[j]);
156#endif /* CONFIG_COMPAT */
157 } else {
158 for (j = 0; hwcap_str[j]; j++)
159 if (elf_hwcap & (1 << j))
160 seq_printf(m, " %s", hwcap_str[j]);
161 }
162 seq_puts(m, "\n");
163
164 seq_printf(m, "CPU implementer\t: 0x%02x\n",
165 MIDR_IMPLEMENTOR(midr));
166 seq_printf(m, "CPU architecture: 8\n");
167 seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
168 seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
169 seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
170 }
171
172 return 0;
173}
174
175static void *c_start(struct seq_file *m, loff_t *pos)
176{
177 return *pos < 1 ? (void *)1 : NULL;
178}
179
180static void *c_next(struct seq_file *m, void *v, loff_t *pos)
181{
182 ++*pos;
183 return NULL;
184}
185
186static void c_stop(struct seq_file *m, void *v)
187{
188}
189
190const struct seq_operations cpuinfo_op = {
191 .start = c_start,
192 .next = c_next,
193 .stop = c_stop,
194 .show = c_show
195};
196
Steve Capperf8d9f922016-07-08 16:01:13 +0100197
198static struct kobj_type cpuregs_kobj_type = {
199 .sysfs_ops = &kobj_sysfs_ops,
200};
201
202/*
203 * The ARM ARM uses the phrase "32-bit register" to describe a register
204 * whose upper 32 bits are RES0 (per C5.1.1, ARM DDI 0487A.i), however
205 * no statement is made as to whether the upper 32 bits will or will not
206 * be made use of in future, and between ARM DDI 0487A.c and ARM DDI
207 * 0487A.d CLIDR_EL1 was expanded from 32-bit to 64-bit.
208 *
209 * Thus, while both MIDR_EL1 and REVIDR_EL1 are described as 32-bit
210 * registers, we expose them both as 64 bit values to cater for possible
211 * future expansion without an ABI break.
212 */
213#define kobj_to_cpuinfo(kobj) container_of(kobj, struct cpuinfo_arm64, kobj)
214#define CPUREGS_ATTR_RO(_name, _field) \
215 static ssize_t _name##_show(struct kobject *kobj, \
216 struct kobj_attribute *attr, char *buf) \
217 { \
218 struct cpuinfo_arm64 *info = kobj_to_cpuinfo(kobj); \
219 \
220 if (info->reg_midr) \
221 return sprintf(buf, "0x%016x\n", info->reg_##_field); \
222 else \
223 return 0; \
224 } \
225 static struct kobj_attribute cpuregs_attr_##_name = __ATTR_RO(_name)
226
227CPUREGS_ATTR_RO(midr_el1, midr);
228CPUREGS_ATTR_RO(revidr_el1, revidr);
229
230static struct attribute *cpuregs_id_attrs[] = {
231 &cpuregs_attr_midr_el1.attr,
232 &cpuregs_attr_revidr_el1.attr,
233 NULL
234};
235
Arvind Yadav70a62ad2017-06-30 17:00:20 +0530236static const struct attribute_group cpuregs_attr_group = {
Steve Capperf8d9f922016-07-08 16:01:13 +0100237 .attrs = cpuregs_id_attrs,
238 .name = "identification"
239};
240
Anna-Maria Gleixnera7ce95e2016-11-27 00:13:44 +0100241static int cpuid_cpu_online(unsigned int cpu)
Steve Capperf8d9f922016-07-08 16:01:13 +0100242{
243 int rc;
244 struct device *dev;
245 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
246
247 dev = get_cpu_device(cpu);
248 if (!dev) {
249 rc = -ENODEV;
250 goto out;
251 }
252 rc = kobject_add(&info->kobj, &dev->kobj, "regs");
253 if (rc)
254 goto out;
255 rc = sysfs_create_group(&info->kobj, &cpuregs_attr_group);
256 if (rc)
257 kobject_del(&info->kobj);
258out:
259 return rc;
260}
261
Anna-Maria Gleixnera7ce95e2016-11-27 00:13:44 +0100262static int cpuid_cpu_offline(unsigned int cpu)
Steve Capperf8d9f922016-07-08 16:01:13 +0100263{
264 struct device *dev;
265 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
266
267 dev = get_cpu_device(cpu);
268 if (!dev)
269 return -ENODEV;
270 if (info->kobj.parent) {
271 sysfs_remove_group(&info->kobj, &cpuregs_attr_group);
272 kobject_del(&info->kobj);
273 }
274
275 return 0;
276}
277
Steve Capperf8d9f922016-07-08 16:01:13 +0100278static int __init cpuinfo_regs_init(void)
279{
Anna-Maria Gleixnera7ce95e2016-11-27 00:13:44 +0100280 int cpu, ret;
Steve Capperf8d9f922016-07-08 16:01:13 +0100281
282 for_each_possible_cpu(cpu) {
283 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
284
285 kobject_init(&info->kobj, &cpuregs_kobj_type);
Steve Capperf8d9f922016-07-08 16:01:13 +0100286 }
Steve Capperf8d9f922016-07-08 16:01:13 +0100287
Anna-Maria Gleixnera7ce95e2016-11-27 00:13:44 +0100288 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "arm64/cpuinfo:online",
289 cpuid_cpu_online, cpuid_cpu_offline);
290 if (ret < 0) {
291 pr_err("cpuinfo: failed to register hotplug callbacks.\n");
292 return ret;
293 }
Steve Capperf8d9f922016-07-08 16:01:13 +0100294 return 0;
295}
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100296static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
297{
298 unsigned int cpu = smp_processor_id();
299 u32 l1ip = CTR_L1IP(info->reg_ctr);
300
Will Deacon3689c752017-03-10 20:32:20 +0000301 switch (l1ip) {
302 case ICACHE_POLICY_PIPT:
303 break;
Will Deacondda288d2017-03-10 20:32:24 +0000304 case ICACHE_POLICY_VPIPT:
305 set_bit(ICACHEF_VPIPT, &__icache_flags);
306 break;
Will Deacon3689c752017-03-10 20:32:20 +0000307 default:
Will Deacon3689c752017-03-10 20:32:20 +0000308 /* Fallthrough */
309 case ICACHE_POLICY_VIPT:
310 /* Assume aliasing */
311 set_bit(ICACHEF_ALIASING, &__icache_flags);
312 }
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100313
Mark Rutlandea171962014-08-01 10:23:20 +0100314 pr_info("Detected %s I-cache on CPU%d\n", icache_policy_str[l1ip], cpu);
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100315}
316
Mark Rutlanddf857412014-07-16 16:32:44 +0100317static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
318{
319 info->reg_cntfrq = arch_timer_get_cntfrq();
320 info->reg_ctr = read_cpuid_cachetype();
Mark Rutland1cc6ed92016-03-04 12:54:05 +0000321 info->reg_dczid = read_cpuid(DCZID_EL0);
Mark Rutlanddf857412014-07-16 16:32:44 +0100322 info->reg_midr = read_cpuid_id();
Steve Capperf8d9f922016-07-08 16:01:13 +0100323 info->reg_revidr = read_cpuid(REVIDR_EL1);
Mark Rutlanddf857412014-07-16 16:32:44 +0100324
Mark Rutland1cc6ed92016-03-04 12:54:05 +0000325 info->reg_id_aa64dfr0 = read_cpuid(ID_AA64DFR0_EL1);
326 info->reg_id_aa64dfr1 = read_cpuid(ID_AA64DFR1_EL1);
327 info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1);
328 info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1);
329 info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);
330 info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
331 info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);
332 info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1);
333 info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
Mark Rutlanddf857412014-07-16 16:32:44 +0100334
Suzuki K Poulosea6dc3cd2016-04-18 10:28:35 +0100335 /* Update the 32bit ID registers only if AArch32 is implemented */
336 if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
337 info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
338 info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
339 info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
340 info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
341 info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
342 info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
343 info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
344 info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
345 info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
346 info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
347 info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
348 info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
349 info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100350
Suzuki K Poulosea6dc3cd2016-04-18 10:28:35 +0100351 info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
352 info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
353 info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
354 }
Mark Rutland80639d42015-01-07 10:31:56 +0000355
Mark Rutland59ccc0d2014-07-16 16:32:45 +0100356 cpuinfo_detect_icache_policy(info);
Mark Rutlanddf857412014-07-16 16:32:44 +0100357}
358
359void cpuinfo_store_cpu(void)
360{
361 struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data);
362 __cpuinfo_store_cpu(info);
Suzuki K. Poulose3086d392015-10-19 14:24:46 +0100363 update_cpu_features(smp_processor_id(), info, &boot_cpu_data);
Mark Rutlanddf857412014-07-16 16:32:44 +0100364}
365
366void __init cpuinfo_store_boot_cpu(void)
367{
368 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, 0);
369 __cpuinfo_store_cpu(info);
370
371 boot_cpu_data = *info;
Suzuki K. Poulose3c739b52015-10-19 14:24:45 +0100372 init_cpu_features(&boot_cpu_data);
Mark Rutlanddf857412014-07-16 16:32:44 +0100373}
Steve Capperf8d9f922016-07-08 16:01:13 +0100374
375device_initcall(cpuinfo_regs_init);