xref: /linux/arch/loongarch/kernel/cpu-probe.c (revision 7a5f1cd22d47f8ca4b760b6334378ae42c1bd24b)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Processor capabilities determination functions.
4  *
5  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6  */
7 #include <linux/init.h>
8 #include <linux/kernel.h>
9 #include <linux/ptrace.h>
10 #include <linux/cpu.h>
11 #include <linux/smp.h>
12 #include <linux/stddef.h>
13 #include <linux/export.h>
14 #include <linux/printk.h>
15 #include <linux/uaccess.h>
16 
17 #include <asm/cpu-features.h>
18 #include <asm/elf.h>
19 #include <asm/fpu.h>
20 #include <asm/loongarch.h>
21 #include <asm/pgtable-bits.h>
22 #include <asm/setup.h>
23 
24 /* Hardware capabilities */
25 unsigned int elf_hwcap __read_mostly;
26 EXPORT_SYMBOL_GPL(elf_hwcap);
27 
28 /*
29  * Determine the FCSR mask for FPU hardware.
30  */
31 static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_loongarch *c)
32 {
33 	unsigned long sr, mask, fcsr, fcsr0, fcsr1;
34 
35 	fcsr = c->fpu_csr0;
36 	mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;
37 
38 	sr = read_csr_euen();
39 	enable_fpu();
40 
41 	fcsr0 = fcsr & mask;
42 	write_fcsr(LOONGARCH_FCSR0, fcsr0);
43 	fcsr0 = read_fcsr(LOONGARCH_FCSR0);
44 
45 	fcsr1 = fcsr | ~mask;
46 	write_fcsr(LOONGARCH_FCSR0, fcsr1);
47 	fcsr1 = read_fcsr(LOONGARCH_FCSR0);
48 
49 	write_fcsr(LOONGARCH_FCSR0, fcsr);
50 
51 	write_csr_euen(sr);
52 
53 	c->fpu_mask = ~(fcsr0 ^ fcsr1) & ~mask;
54 }
55 
56 /* simd = -1/0/128/256 */
57 static unsigned int simd = -1U;
58 
59 static int __init cpu_setup_simd(char *str)
60 {
61 	get_option(&str, &simd);
62 	pr_info("Set SIMD width = %u\n", simd);
63 
64 	return 0;
65 }
66 
67 early_param("simd", cpu_setup_simd);
68 
69 static int __init cpu_final_simd(void)
70 {
71 	struct cpuinfo_loongarch *c = &cpu_data[0];
72 
73 	if (simd < 128) {
74 		c->options &= ~LOONGARCH_CPU_LSX;
75 		elf_hwcap &= ~HWCAP_LOONGARCH_LSX;
76 	}
77 
78 	if (simd < 256) {
79 		c->options &= ~LOONGARCH_CPU_LASX;
80 		elf_hwcap &= ~HWCAP_LOONGARCH_LASX;
81 	}
82 
83 	simd = 0;
84 
85 	if (c->options & LOONGARCH_CPU_LSX)
86 		simd = 128;
87 
88 	if (c->options & LOONGARCH_CPU_LASX)
89 		simd = 256;
90 
91 	pr_info("Final SIMD width = %u\n", simd);
92 
93 	return 0;
94 }
95 
96 arch_initcall(cpu_final_simd);
97 
98 static inline void set_elf_platform(int cpu, const char *plat)
99 {
100 	if (cpu == 0)
101 		__elf_platform = plat;
102 }
103 
104 /* MAP BASE */
105 unsigned long vm_map_base;
106 EXPORT_SYMBOL(vm_map_base);
107 
108 static void cpu_probe_addrbits(struct cpuinfo_loongarch *c)
109 {
110 #ifdef CONFIG_32BIT
111 	c->pabits = cpu_pabits;
112 	c->vabits = cpu_vabits;
113 	vm_map_base = KVRANGE;
114 #else
115 	c->pabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_PABITS) >> 4;
116 	c->vabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_VABITS) >> 12;
117 	vm_map_base = 0UL - (1UL << c->vabits);
118 #endif
119 }
120 
121 static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa)
122 {
123 	switch (isa) {
124 	case LOONGARCH_CPU_ISA_LA64:
125 		c->isa_level |= LOONGARCH_CPU_ISA_LA64;
126 		fallthrough;
127 	case LOONGARCH_CPU_ISA_LA32S:
128 		c->isa_level |= LOONGARCH_CPU_ISA_LA32S;
129 		fallthrough;
130 	case LOONGARCH_CPU_ISA_LA32R:
131 		c->isa_level |= LOONGARCH_CPU_ISA_LA32R;
132 		break;
133 	}
134 }
135 
136 static void cpu_probe_common(struct cpuinfo_loongarch *c)
137 {
138 	unsigned int config;
139 	unsigned long asid_mask;
140 
141 	c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR | LOONGARCH_CPU_VINT;
142 
143 	elf_hwcap = HWCAP_LOONGARCH_CPUCFG;
144 
145 	config = read_cpucfg(LOONGARCH_CPUCFG1);
146 
147 	switch (config & CPUCFG1_ISA) {
148 	case 0:
149 		set_isa(c, LOONGARCH_CPU_ISA_LA32R);
150 		break;
151 	case 1:
152 		set_isa(c, LOONGARCH_CPU_ISA_LA32S);
153 		break;
154 	case 2:
155 		set_isa(c, LOONGARCH_CPU_ISA_LA64);
156 		break;
157 	default:
158 		pr_warn("Warning: unknown ISA level\n");
159 	}
160 
161 	if (config & CPUCFG1_PAGING)
162 		c->options |= LOONGARCH_CPU_TLB;
163 	if (config & CPUCFG1_IOCSR)
164 		c->options |= LOONGARCH_CPU_IOCSR;
165 	if (config & CPUCFG1_MSGINT)
166 		c->options |= LOONGARCH_CPU_MSGINT;
167 	if (config & CPUCFG1_UAL) {
168 		c->options |= LOONGARCH_CPU_UAL;
169 		elf_hwcap |= HWCAP_LOONGARCH_UAL;
170 	}
171 	if (config & CPUCFG1_CRC32) {
172 		c->options |= LOONGARCH_CPU_CRC32;
173 		elf_hwcap |= HWCAP_LOONGARCH_CRC32;
174 	}
175 
176 	config = read_cpucfg(LOONGARCH_CPUCFG2);
177 	if (config & CPUCFG2_LAM) {
178 		c->options |= LOONGARCH_CPU_LAM;
179 		elf_hwcap |= HWCAP_LOONGARCH_LAM;
180 	}
181 	if (config & CPUCFG2_LAM_BH) {
182 		c->options |= LOONGARCH_CPU_LAM_BH;
183 		elf_hwcap |= HWCAP_LOONGARCH_LAM_BH;
184 	}
185 	if (config & CPUCFG2_SCQ) {
186 		c->options |= LOONGARCH_CPU_SCQ;
187 		elf_hwcap |= HWCAP_LOONGARCH_SCQ;
188 	}
189 	if (config & CPUCFG2_FP) {
190 		c->options |= LOONGARCH_CPU_FPU;
191 		elf_hwcap |= HWCAP_LOONGARCH_FPU;
192 	}
193 #ifdef CONFIG_CPU_HAS_LSX
194 	if ((config & CPUCFG2_LSX) && (simd >= 128)) {
195 		c->options |= LOONGARCH_CPU_LSX;
196 		elf_hwcap |= HWCAP_LOONGARCH_LSX;
197 	}
198 #endif
199 #ifdef CONFIG_CPU_HAS_LASX
200 	if ((config & CPUCFG2_LASX) && (simd >= 256)) {
201 		c->options |= LOONGARCH_CPU_LASX;
202 		elf_hwcap |= HWCAP_LOONGARCH_LASX;
203 	}
204 #endif
205 	if (config & CPUCFG2_COMPLEX) {
206 		c->options |= LOONGARCH_CPU_COMPLEX;
207 		elf_hwcap |= HWCAP_LOONGARCH_COMPLEX;
208 	}
209 	if (config & CPUCFG2_CRYPTO) {
210 		c->options |= LOONGARCH_CPU_CRYPTO;
211 		elf_hwcap |= HWCAP_LOONGARCH_CRYPTO;
212 	}
213 	if (config & CPUCFG2_PTW) {
214 		c->options |= LOONGARCH_CPU_PTW;
215 		elf_hwcap |= HWCAP_LOONGARCH_PTW;
216 	}
217 	if (config & CPUCFG2_LSPW) {
218 		c->options |= LOONGARCH_CPU_LSPW;
219 		elf_hwcap |= HWCAP_LOONGARCH_LSPW;
220 	}
221 	if (config & CPUCFG2_LVZP) {
222 		c->options |= LOONGARCH_CPU_LVZ;
223 		elf_hwcap |= HWCAP_LOONGARCH_LVZ;
224 	}
225 #ifdef CONFIG_CPU_HAS_LBT
226 	if (config & CPUCFG2_X86BT) {
227 		c->options |= LOONGARCH_CPU_LBT_X86;
228 		elf_hwcap |= HWCAP_LOONGARCH_LBT_X86;
229 	}
230 	if (config & CPUCFG2_ARMBT) {
231 		c->options |= LOONGARCH_CPU_LBT_ARM;
232 		elf_hwcap |= HWCAP_LOONGARCH_LBT_ARM;
233 	}
234 	if (config & CPUCFG2_MIPSBT) {
235 		c->options |= LOONGARCH_CPU_LBT_MIPS;
236 		elf_hwcap |= HWCAP_LOONGARCH_LBT_MIPS;
237 	}
238 #endif
239 
240 	config = read_cpucfg(LOONGARCH_CPUCFG6);
241 	if (config & CPUCFG6_PMP)
242 		c->options |= LOONGARCH_CPU_PMP;
243 
244 	config = csr_read32(LOONGARCH_CSR_ASID);
245 	config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT;
246 	asid_mask = GENMASK(config - 1, 0);
247 	set_cpu_asid_mask(c, asid_mask);
248 
249 	config = read_csr_prcfg1();
250 	c->timerbits = (config & CSR_CONF1_TMRBITS) >> CSR_CONF1_TMRBITS_SHIFT;
251 	c->ksave_mask = GENMASK((config & CSR_CONF1_KSNUM) - 1, 0);
252 	c->ksave_mask &= ~(EXC_KSAVE_MASK | PERCPU_KSAVE_MASK | KVM_KSAVE_MASK);
253 
254 	config = read_csr_prcfg3();
255 	switch (config & CSR_CONF3_TLBTYPE) {
256 	case 0:
257 		c->tlbsizemtlb = 0;
258 		c->tlbsizestlbsets = 0;
259 		c->tlbsizestlbways = 0;
260 		c->tlbsize = 0;
261 		break;
262 	case 1:
263 		c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
264 		c->tlbsizestlbsets = 0;
265 		c->tlbsizestlbways = 0;
266 		c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
267 		break;
268 	case 2:
269 		c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
270 		c->tlbsizestlbsets = 1 << ((config & CSR_CONF3_STLBIDX) >> CSR_CONF3_STLBIDX_SHIFT);
271 		c->tlbsizestlbways = ((config & CSR_CONF3_STLBWAYS) >> CSR_CONF3_STLBWAYS_SHIFT) + 1;
272 		c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
273 		break;
274 	default:
275 		pr_warn("Warning: unknown TLB type\n");
276 	}
277 
278 	if (get_num_brps() + get_num_wrps())
279 		c->options |= LOONGARCH_CPU_WATCH;
280 }
281 
282 #define MAX_NAME_LEN	32
283 #define VENDOR_OFFSET	0
284 #define CPUNAME_OFFSET	9
285 
286 static char cpu_full_name[MAX_NAME_LEN] = "        -        ";
287 
288 static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int cpu)
289 {
290 	uint32_t config;
291 	uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]);
292 	uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]);
293 	const char *core_name = id_to_core_name(c->processor_id);
294 
295 	switch (BIT(fls(c->isa_level) - 1)) {
296 	case LOONGARCH_CPU_ISA_LA32R:
297 	case LOONGARCH_CPU_ISA_LA32S:
298 		c->cputype = CPU_LOONGSON32;
299 		__cpu_family[cpu] = "Loongson-32bit";
300 		break;
301 	case LOONGARCH_CPU_ISA_LA64:
302 		c->cputype = CPU_LOONGSON64;
303 		__cpu_family[cpu] = "Loongson-64bit";
304 		break;
305 	}
306 
307 	pr_info("%s Processor probed (%s Core)\n", __cpu_family[cpu], core_name);
308 
309 	if (!cpu_has_iocsr) {
310 		__cpu_full_name[cpu] = "Unknown";
311 		return;
312 	}
313 
314 #ifdef CONFIG_64BIT
315 	*vendor = iocsr_read64(LOONGARCH_IOCSR_VENDOR);
316 	*cpuname = iocsr_read64(LOONGARCH_IOCSR_CPUNAME);
317 #else
318 	*vendor = iocsr_read32(LOONGARCH_IOCSR_VENDOR) |
319 		(u64)iocsr_read32(LOONGARCH_IOCSR_VENDOR + 4) << 32;
320 	*cpuname = iocsr_read32(LOONGARCH_IOCSR_CPUNAME) |
321 		(u64)iocsr_read32(LOONGARCH_IOCSR_CPUNAME + 4) << 32;
322 #endif
323 
324 	if (!__cpu_full_name[cpu]) {
325 		if (((char *)vendor)[0] == 0)
326 			__cpu_full_name[cpu] = "Unknown";
327 		else
328 			__cpu_full_name[cpu] = cpu_full_name;
329 	}
330 
331 	config = iocsr_read32(LOONGARCH_IOCSR_FEATURES);
332 	if (config & IOCSRF_CSRIPI)
333 		c->options |= LOONGARCH_CPU_CSRIPI;
334 	if (config & IOCSRF_EXTIOI)
335 		c->options |= LOONGARCH_CPU_EXTIOI;
336 	if (config & IOCSRF_FREQSCALE)
337 		c->options |= LOONGARCH_CPU_SCALEFREQ;
338 	if (config & IOCSRF_FLATMODE)
339 		c->options |= LOONGARCH_CPU_FLATMODE;
340 	if (config & IOCSRF_EIODECODE)
341 		c->options |= LOONGARCH_CPU_EIODECODE;
342 	if (config & IOCSRF_AVEC)
343 		c->options |= LOONGARCH_CPU_AVECINT;
344 	if (config & IOCSRF_REDIRECT)
345 		c->options |= LOONGARCH_CPU_REDIRECTINT;
346 	if (config & IOCSRF_VM)
347 		c->options |= LOONGARCH_CPU_HYPERVISOR;
348 }
349 
350 #ifdef CONFIG_64BIT
351 /* For use by uaccess.h */
352 u64 __ua_limit;
353 EXPORT_SYMBOL(__ua_limit);
354 #endif
355 
356 const char *__cpu_family[NR_CPUS];
357 const char *__cpu_full_name[NR_CPUS];
358 const char *__elf_platform;
359 
360 static void cpu_report(void)
361 {
362 	struct cpuinfo_loongarch *c = &current_cpu_data;
363 
364 	pr_info("CPU%d revision is: %08x (%s)\n",
365 		smp_processor_id(), c->processor_id, cpu_family_string());
366 	if (c->options & LOONGARCH_CPU_FPU)
367 		pr_info("FPU%d revision is: %08x\n", smp_processor_id(), c->fpu_vers);
368 }
369 
370 void cpu_probe(void)
371 {
372 	unsigned int cpu = smp_processor_id();
373 	struct cpuinfo_loongarch *c = &current_cpu_data;
374 
375 	/*
376 	 * Set a default ELF platform, cpu probe may later
377 	 * overwrite it with a more precise value
378 	 */
379 	set_elf_platform(cpu, "loongarch");
380 
381 	c->cputype	= CPU_UNKNOWN;
382 	c->processor_id = read_cpucfg(LOONGARCH_CPUCFG0);
383 	c->fpu_vers     = (read_cpucfg(LOONGARCH_CPUCFG2) & CPUCFG2_FPVERS) >> 3;
384 
385 	c->fpu_csr0	= FPU_CSR_RN;
386 	c->fpu_mask	= FPU_CSR_RSVD;
387 
388 	cpu_probe_common(c);
389 
390 	per_cpu_trap_init(cpu);
391 
392 	switch (c->processor_id & PRID_COMP_MASK) {
393 	case PRID_COMP_LOONGSON:
394 		cpu_probe_loongson(c, cpu);
395 		break;
396 	}
397 
398 	BUG_ON(!__cpu_family[cpu]);
399 	BUG_ON(c->cputype == CPU_UNKNOWN);
400 
401 	cpu_probe_addrbits(c);
402 
403 #ifdef CONFIG_64BIT
404 	if (cpu == 0)
405 		__ua_limit = ~((1ull << cpu_vabits) - 1);
406 #endif
407 
408 	cpu_report();
409 }
410 
411 ssize_t cpu_show_spectre_v1(struct device *dev,
412 			    struct device_attribute *attr, char *buf)
413 {
414 	return sysfs_emit(buf, "Mitigation: __user pointer sanitization\n");
415 }
416