]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - arch/arm64/kernel/cpuinfo.c
arm64: cpuinfo: constify attribute_group structures.
[mirror_ubuntu-bionic-kernel.git] / arch / arm64 / kernel / cpuinfo.c
CommitLineData
df857416
MR
1/*
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>
02f7760e 18#include <asm/cache.h>
df857416
MR
19#include <asm/cpu.h>
20#include <asm/cputype.h>
e116a375 21#include <asm/cpufeature.h>
df857416 22
59ccc0d4 23#include <linux/bitops.h>
80c517b0 24#include <linux/bug.h>
e47b020a
CM
25#include <linux/compat.h>
26#include <linux/elf.h>
df857416 27#include <linux/init.h>
127161aa 28#include <linux/kernel.h>
12d11817 29#include <linux/personality.h>
80c517b0 30#include <linux/preempt.h>
59ccc0d4 31#include <linux/printk.h>
12d11817
SP
32#include <linux/seq_file.h>
33#include <linux/sched.h>
df857416 34#include <linux/smp.h>
92e788b7 35#include <linux/delay.h>
df857416
MR
36
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
59ccc0d4 45static char *icache_policy_str[] = {
155433cb
WD
46 [0 ... ICACHE_POLICY_PIPT] = "RESERVED/UNKNOWN",
47 [ICACHE_POLICY_VIPT] = "VIPT",
48 [ICACHE_POLICY_PIPT] = "PIPT",
dda288d7 49 [ICACHE_POLICY_VPIPT] = "VPIPT",
59ccc0d4
MR
50};
51
52unsigned long __icache_flags;
53
9299b247 54static const char *const hwcap_str[] = {
12d11817
SP
55 "fp",
56 "asimd",
57 "evtstrm",
58 "aes",
59 "pmull",
60 "sha1",
61 "sha2",
62 "crc32",
63 "atomics",
bf500618
SP
64 "fphp",
65 "asimdhp",
77c97b4e 66 "cpuid",
f92f5ce0 67 "asimdrdm",
c8c3798d 68 "jscvt",
cb567e79 69 "fcma",
c651aae5 70 "lrcpc",
12d11817
SP
71 NULL
72};
73
74#ifdef CONFIG_COMPAT
9299b247 75static const char *const compat_hwcap_str[] = {
12d11817
SP
76 "swp",
77 "half",
78 "thumb",
79 "26bit",
80 "fastmult",
81 "fpa",
82 "vfp",
83 "edsp",
84 "java",
85 "iwmmxt",
86 "crunch",
87 "thumbee",
88 "neon",
89 "vfpv3",
90 "vfpv3d16",
91 "tls",
92 "vfpv4",
93 "idiva",
94 "idivt",
95 "vfpd32",
96 "lpae",
f228b494
JG
97 "evtstrm",
98 NULL
12d11817
SP
99};
100
9299b247 101static const char *const compat_hwcap2_str[] = {
12d11817
SP
102 "aes",
103 "pmull",
104 "sha1",
105 "sha2",
106 "crc32",
107 NULL
108};
109#endif /* CONFIG_COMPAT */
110
111static int c_show(struct seq_file *m, void *v)
112{
113 int i, j;
e47b020a 114 bool compat = personality(current->personality) == PER_LINUX32;
12d11817
SP
115
116 for_each_online_cpu(i) {
117 struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
118 u32 midr = cpuinfo->reg_midr;
119
120 /*
121 * glibc reads /proc/cpuinfo to determine the number of
122 * online processors, looking for lines beginning with
123 * "processor". Give glibc what it expects.
124 */
125 seq_printf(m, "processor\t: %d\n", i);
e47b020a
CM
126 if (compat)
127 seq_printf(m, "model name\t: ARMv8 Processor rev %d (%s)\n",
128 MIDR_REVISION(midr), COMPAT_ELF_PLATFORM);
12d11817 129
92e788b7
YS
130 seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
131 loops_per_jiffy / (500000UL/HZ),
132 loops_per_jiffy / (5000UL/HZ) % 100);
133
12d11817
SP
134 /*
135 * Dump out the common processor features in a single line.
136 * Userspace should read the hwcaps with getauxval(AT_HWCAP)
137 * rather than attempting to parse this, but there's a body of
138 * software which does already (at least for 32-bit).
139 */
140 seq_puts(m, "Features\t:");
e47b020a 141 if (compat) {
12d11817
SP
142#ifdef CONFIG_COMPAT
143 for (j = 0; compat_hwcap_str[j]; j++)
144 if (compat_elf_hwcap & (1 << j))
145 seq_printf(m, " %s", compat_hwcap_str[j]);
146
147 for (j = 0; compat_hwcap2_str[j]; j++)
148 if (compat_elf_hwcap2 & (1 << j))
149 seq_printf(m, " %s", compat_hwcap2_str[j]);
150#endif /* CONFIG_COMPAT */
151 } else {
152 for (j = 0; hwcap_str[j]; j++)
153 if (elf_hwcap & (1 << j))
154 seq_printf(m, " %s", hwcap_str[j]);
155 }
156 seq_puts(m, "\n");
157
158 seq_printf(m, "CPU implementer\t: 0x%02x\n",
159 MIDR_IMPLEMENTOR(midr));
160 seq_printf(m, "CPU architecture: 8\n");
161 seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
162 seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
163 seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
164 }
165
166 return 0;
167}
168
169static void *c_start(struct seq_file *m, loff_t *pos)
170{
171 return *pos < 1 ? (void *)1 : NULL;
172}
173
174static void *c_next(struct seq_file *m, void *v, loff_t *pos)
175{
176 ++*pos;
177 return NULL;
178}
179
180static void c_stop(struct seq_file *m, void *v)
181{
182}
183
184const struct seq_operations cpuinfo_op = {
185 .start = c_start,
186 .next = c_next,
187 .stop = c_stop,
188 .show = c_show
189};
190
f8d9f924
SC
191
192static struct kobj_type cpuregs_kobj_type = {
193 .sysfs_ops = &kobj_sysfs_ops,
194};
195
196/*
197 * The ARM ARM uses the phrase "32-bit register" to describe a register
198 * whose upper 32 bits are RES0 (per C5.1.1, ARM DDI 0487A.i), however
199 * no statement is made as to whether the upper 32 bits will or will not
200 * be made use of in future, and between ARM DDI 0487A.c and ARM DDI
201 * 0487A.d CLIDR_EL1 was expanded from 32-bit to 64-bit.
202 *
203 * Thus, while both MIDR_EL1 and REVIDR_EL1 are described as 32-bit
204 * registers, we expose them both as 64 bit values to cater for possible
205 * future expansion without an ABI break.
206 */
207#define kobj_to_cpuinfo(kobj) container_of(kobj, struct cpuinfo_arm64, kobj)
208#define CPUREGS_ATTR_RO(_name, _field) \
209 static ssize_t _name##_show(struct kobject *kobj, \
210 struct kobj_attribute *attr, char *buf) \
211 { \
212 struct cpuinfo_arm64 *info = kobj_to_cpuinfo(kobj); \
213 \
214 if (info->reg_midr) \
215 return sprintf(buf, "0x%016x\n", info->reg_##_field); \
216 else \
217 return 0; \
218 } \
219 static struct kobj_attribute cpuregs_attr_##_name = __ATTR_RO(_name)
220
221CPUREGS_ATTR_RO(midr_el1, midr);
222CPUREGS_ATTR_RO(revidr_el1, revidr);
223
224static struct attribute *cpuregs_id_attrs[] = {
225 &cpuregs_attr_midr_el1.attr,
226 &cpuregs_attr_revidr_el1.attr,
227 NULL
228};
229
70a62ad1 230static const struct attribute_group cpuregs_attr_group = {
f8d9f924
SC
231 .attrs = cpuregs_id_attrs,
232 .name = "identification"
233};
234
a7ce95e1 235static int cpuid_cpu_online(unsigned int cpu)
f8d9f924
SC
236{
237 int rc;
238 struct device *dev;
239 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
240
241 dev = get_cpu_device(cpu);
242 if (!dev) {
243 rc = -ENODEV;
244 goto out;
245 }
246 rc = kobject_add(&info->kobj, &dev->kobj, "regs");
247 if (rc)
248 goto out;
249 rc = sysfs_create_group(&info->kobj, &cpuregs_attr_group);
250 if (rc)
251 kobject_del(&info->kobj);
252out:
253 return rc;
254}
255
a7ce95e1 256static int cpuid_cpu_offline(unsigned int cpu)
f8d9f924
SC
257{
258 struct device *dev;
259 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
260
261 dev = get_cpu_device(cpu);
262 if (!dev)
263 return -ENODEV;
264 if (info->kobj.parent) {
265 sysfs_remove_group(&info->kobj, &cpuregs_attr_group);
266 kobject_del(&info->kobj);
267 }
268
269 return 0;
270}
271
f8d9f924
SC
272static int __init cpuinfo_regs_init(void)
273{
a7ce95e1 274 int cpu, ret;
f8d9f924
SC
275
276 for_each_possible_cpu(cpu) {
277 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, cpu);
278
279 kobject_init(&info->kobj, &cpuregs_kobj_type);
f8d9f924 280 }
f8d9f924 281
a7ce95e1
AMG
282 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "arm64/cpuinfo:online",
283 cpuid_cpu_online, cpuid_cpu_offline);
284 if (ret < 0) {
285 pr_err("cpuinfo: failed to register hotplug callbacks.\n");
286 return ret;
287 }
f8d9f924
SC
288 return 0;
289}
59ccc0d4
MR
290static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
291{
292 unsigned int cpu = smp_processor_id();
293 u32 l1ip = CTR_L1IP(info->reg_ctr);
294
3689c75a
WD
295 switch (l1ip) {
296 case ICACHE_POLICY_PIPT:
297 break;
dda288d7
WD
298 case ICACHE_POLICY_VPIPT:
299 set_bit(ICACHEF_VPIPT, &__icache_flags);
300 break;
3689c75a 301 default:
3689c75a
WD
302 /* Fallthrough */
303 case ICACHE_POLICY_VIPT:
304 /* Assume aliasing */
305 set_bit(ICACHEF_ALIASING, &__icache_flags);
306 }
59ccc0d4 307
ea171967 308 pr_info("Detected %s I-cache on CPU%d\n", icache_policy_str[l1ip], cpu);
59ccc0d4
MR
309}
310
df857416
MR
311static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
312{
313 info->reg_cntfrq = arch_timer_get_cntfrq();
314 info->reg_ctr = read_cpuid_cachetype();
1cc6ed90 315 info->reg_dczid = read_cpuid(DCZID_EL0);
df857416 316 info->reg_midr = read_cpuid_id();
f8d9f924 317 info->reg_revidr = read_cpuid(REVIDR_EL1);
df857416 318
1cc6ed90
MR
319 info->reg_id_aa64dfr0 = read_cpuid(ID_AA64DFR0_EL1);
320 info->reg_id_aa64dfr1 = read_cpuid(ID_AA64DFR1_EL1);
321 info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1);
322 info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1);
323 info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1);
324 info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
325 info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1);
326 info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1);
327 info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
328
a6dc3cd7
SP
329 /* Update the 32bit ID registers only if AArch32 is implemented */
330 if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) {
331 info->reg_id_dfr0 = read_cpuid(ID_DFR0_EL1);
332 info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
333 info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
334 info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
335 info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
336 info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
337 info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
338 info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
339 info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
340 info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
341 info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
342 info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
343 info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
344
345 info->reg_mvfr0 = read_cpuid(MVFR0_EL1);
346 info->reg_mvfr1 = read_cpuid(MVFR1_EL1);
347 info->reg_mvfr2 = read_cpuid(MVFR2_EL1);
348 }
80639d4a 349
59ccc0d4 350 cpuinfo_detect_icache_policy(info);
df857416
MR
351}
352
353void cpuinfo_store_cpu(void)
354{
355 struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data);
356 __cpuinfo_store_cpu(info);
3086d391 357 update_cpu_features(smp_processor_id(), info, &boot_cpu_data);
df857416
MR
358}
359
360void __init cpuinfo_store_boot_cpu(void)
361{
362 struct cpuinfo_arm64 *info = &per_cpu(cpu_data, 0);
363 __cpuinfo_store_cpu(info);
364
365 boot_cpu_data = *info;
3c739b57 366 init_cpu_features(&boot_cpu_data);
df857416 367}
f8d9f924
SC
368
369device_initcall(cpuinfo_regs_init);