xref: /linux/arch/riscv/kernel/sys_hwprobe.c (revision d0fc6fab20460add1f27402cd8b945d094a56b21)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * The hwprobe interface, for allowing userspace to probe to see which features
4  * are supported by the hardware.  See Documentation/arch/riscv/hwprobe.rst for
5  * more details.
6  */
7 #include <linux/syscalls.h>
8 #include <asm/cacheflush.h>
9 #include <asm/cpufeature.h>
10 #include <asm/hwprobe.h>
11 #include <asm/processor.h>
12 #include <asm/delay.h>
13 #include <asm/sbi.h>
14 #include <asm/switch_to.h>
15 #include <asm/uaccess.h>
16 #include <asm/unistd.h>
17 #include <asm/vector.h>
18 #include <asm/vendor_extensions/mips_hwprobe.h>
19 #include <asm/vendor_extensions/sifive_hwprobe.h>
20 #include <asm/vendor_extensions/thead_hwprobe.h>
21 #include <vdso/vsyscall.h>
22 
23 
24 #define EXT_KEY(isa_arg, ext, pv, missing)					\
25 	do {										\
26 		if (__riscv_isa_extension_available(isa_arg, RISCV_ISA_EXT_##ext))	\
27 			pv |= RISCV_HWPROBE_EXT_##ext;				\
28 		else									\
29 			missing |= RISCV_HWPROBE_EXT_##ext;				\
30 	} while (false)
31 
32 static void hwprobe_arch_id(struct riscv_hwprobe *pair,
33 			    const struct cpumask *cpus)
34 {
35 	u64 id = -1ULL;
36 	bool first = true;
37 	int cpu;
38 
39 	if (pair->key != RISCV_HWPROBE_KEY_MVENDORID &&
40 	    pair->key != RISCV_HWPROBE_KEY_MIMPID &&
41 	    pair->key != RISCV_HWPROBE_KEY_MARCHID)
42 		goto out;
43 
44 	for_each_cpu(cpu, cpus) {
45 		u64 cpu_id;
46 
47 		switch (pair->key) {
48 		case RISCV_HWPROBE_KEY_MVENDORID:
49 			cpu_id = riscv_cached_mvendorid(cpu);
50 			break;
51 		case RISCV_HWPROBE_KEY_MIMPID:
52 			cpu_id = riscv_cached_mimpid(cpu);
53 			break;
54 		case RISCV_HWPROBE_KEY_MARCHID:
55 			cpu_id = riscv_cached_marchid(cpu);
56 			break;
57 		}
58 
59 		if (first) {
60 			id = cpu_id;
61 			first = false;
62 		}
63 
64 		/*
65 		 * If there's a mismatch for the given set, return -1 in the
66 		 * value.
67 		 */
68 		if (id != cpu_id) {
69 			id = -1ULL;
70 			break;
71 		}
72 	}
73 
74 out:
75 	pair->value = id;
76 }
77 
78 static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
79 			     const struct cpumask *cpus)
80 {
81 	int cpu;
82 	u64 missing = 0;
83 
84 	pair->value = 0;
85 	if (has_fpu())
86 		pair->value |= RISCV_HWPROBE_IMA_FD;
87 
88 	if (riscv_isa_extension_available(NULL, C))
89 		pair->value |= RISCV_HWPROBE_IMA_C;
90 
91 	if (has_vector() && riscv_isa_extension_available(NULL, V))
92 		pair->value |= RISCV_HWPROBE_IMA_V;
93 
94 	/*
95 	 * Loop through and record extensions that 1) anyone has, and 2) anyone
96 	 * doesn't have.
97 	 */
98 	for_each_cpu(cpu, cpus) {
99 		struct riscv_isainfo *isainfo = &hart_isa[cpu];
100 
101 		/*
102 		 * Only use EXT_KEY() for extensions which can be exposed to userspace,
103 		 * regardless of the kernel's configuration, as no other checks, besides
104 		 * presence in the hart_isa bitmap, are made.
105 		 */
106 		EXT_KEY(isainfo->isa, ZAAMO, pair->value, missing);
107 		EXT_KEY(isainfo->isa, ZABHA, pair->value, missing);
108 		EXT_KEY(isainfo->isa, ZACAS, pair->value, missing);
109 		EXT_KEY(isainfo->isa, ZALASR, pair->value, missing);
110 		EXT_KEY(isainfo->isa, ZALRSC, pair->value, missing);
111 		EXT_KEY(isainfo->isa, ZAWRS, pair->value, missing);
112 		EXT_KEY(isainfo->isa, ZBA, pair->value, missing);
113 		EXT_KEY(isainfo->isa, ZBB, pair->value, missing);
114 		EXT_KEY(isainfo->isa, ZBC, pair->value, missing);
115 		EXT_KEY(isainfo->isa, ZBKB, pair->value, missing);
116 		EXT_KEY(isainfo->isa, ZBKC, pair->value, missing);
117 		EXT_KEY(isainfo->isa, ZBKX, pair->value, missing);
118 		EXT_KEY(isainfo->isa, ZBS, pair->value, missing);
119 		EXT_KEY(isainfo->isa, ZCA, pair->value, missing);
120 		EXT_KEY(isainfo->isa, ZCB, pair->value, missing);
121 		EXT_KEY(isainfo->isa, ZCLSD, pair->value, missing);
122 		EXT_KEY(isainfo->isa, ZCMOP, pair->value, missing);
123 		EXT_KEY(isainfo->isa, ZICBOM, pair->value, missing);
124 		EXT_KEY(isainfo->isa, ZICBOP, pair->value, missing);
125 		EXT_KEY(isainfo->isa, ZICBOZ, pair->value, missing);
126 		EXT_KEY(isainfo->isa, ZICFILP, pair->value, missing);
127 		EXT_KEY(isainfo->isa, ZICNTR, pair->value, missing);
128 		EXT_KEY(isainfo->isa, ZICOND, pair->value, missing);
129 		EXT_KEY(isainfo->isa, ZIHINTNTL, pair->value, missing);
130 		EXT_KEY(isainfo->isa, ZIHINTPAUSE, pair->value, missing);
131 		EXT_KEY(isainfo->isa, ZIHPM, pair->value, missing);
132 		EXT_KEY(isainfo->isa, ZILSD, pair->value, missing);
133 		EXT_KEY(isainfo->isa, ZIMOP, pair->value, missing);
134 		EXT_KEY(isainfo->isa, ZKND, pair->value, missing);
135 		EXT_KEY(isainfo->isa, ZKNE, pair->value, missing);
136 		EXT_KEY(isainfo->isa, ZKNH, pair->value, missing);
137 		EXT_KEY(isainfo->isa, ZKSED, pair->value, missing);
138 		EXT_KEY(isainfo->isa, ZKSH, pair->value, missing);
139 		EXT_KEY(isainfo->isa, ZKT, pair->value, missing);
140 		EXT_KEY(isainfo->isa, ZTSO, pair->value, missing);
141 
142 		/*
143 		 * All the following extensions must depend on the kernel
144 		 * support of V.
145 		 */
146 		if (has_vector()) {
147 			EXT_KEY(isainfo->isa, ZVBB, pair->value, missing);
148 			EXT_KEY(isainfo->isa, ZVBC, pair->value, missing);
149 			EXT_KEY(isainfo->isa, ZVE32F, pair->value, missing);
150 			EXT_KEY(isainfo->isa, ZVE32X, pair->value, missing);
151 			EXT_KEY(isainfo->isa, ZVE64D, pair->value, missing);
152 			EXT_KEY(isainfo->isa, ZVE64F, pair->value, missing);
153 			EXT_KEY(isainfo->isa, ZVE64X, pair->value, missing);
154 			EXT_KEY(isainfo->isa, ZVFBFMIN, pair->value, missing);
155 			EXT_KEY(isainfo->isa, ZVFBFWMA, pair->value, missing);
156 			EXT_KEY(isainfo->isa, ZVFH, pair->value, missing);
157 			EXT_KEY(isainfo->isa, ZVFHMIN, pair->value, missing);
158 			EXT_KEY(isainfo->isa, ZVKB, pair->value, missing);
159 			EXT_KEY(isainfo->isa, ZVKG, pair->value, missing);
160 			EXT_KEY(isainfo->isa, ZVKNED, pair->value, missing);
161 			EXT_KEY(isainfo->isa, ZVKNHA, pair->value, missing);
162 			EXT_KEY(isainfo->isa, ZVKNHB, pair->value, missing);
163 			EXT_KEY(isainfo->isa, ZVKSED, pair->value, missing);
164 			EXT_KEY(isainfo->isa, ZVKSH, pair->value, missing);
165 			EXT_KEY(isainfo->isa, ZVKT, pair->value, missing);
166 		}
167 
168 		EXT_KEY(isainfo->isa, ZCD, pair->value, missing);
169 		EXT_KEY(isainfo->isa, ZCF, pair->value, missing);
170 		EXT_KEY(isainfo->isa, ZFA, pair->value, missing);
171 		EXT_KEY(isainfo->isa, ZFBFMIN, pair->value, missing);
172 		EXT_KEY(isainfo->isa, ZFH, pair->value, missing);
173 		EXT_KEY(isainfo->isa, ZFHMIN, pair->value, missing);
174 
175 		if (IS_ENABLED(CONFIG_RISCV_ISA_SUPM))
176 			EXT_KEY(isainfo->isa, SUPM, pair->value, missing);
177 	}
178 
179 	/* Now turn off reporting features if any CPU is missing it. */
180 	pair->value &= ~missing;
181 }
182 
183 static void hwprobe_isa_ext1(struct riscv_hwprobe *pair,
184 			     const struct cpumask *cpus)
185 {
186 	int cpu;
187 	u64 missing = 0;
188 
189 	pair->value = 0;
190 
191 	/*
192 	 * Loop through and record extensions that 1) anyone has, and 2) anyone
193 	 * doesn't have.
194 	 */
195 	for_each_cpu(cpu, cpus) {
196 		struct riscv_isainfo *isainfo = &hart_isa[cpu];
197 
198 		/*
199 		 * Only use EXT_KEY() for extensions which can be
200 		 * exposed to userspace, regardless of the kernel's
201 		 * configuration, as no other checks, besides presence
202 		 * in the hart_isa bitmap, are made.
203 		 */
204 		EXT_KEY(isainfo->isa, ZICFISS, pair->value, missing);
205 		EXT_KEY(isainfo->isa, ZICCLSM, pair->value, missing);
206 		EXT_KEY(isainfo->isa, ZICCAMOA, pair->value, missing);
207 		EXT_KEY(isainfo->isa, ZICCIF, pair->value, missing);
208 		EXT_KEY(isainfo->isa, ZICCRSE, pair->value, missing);
209 		EXT_KEY(isainfo->isa, ZA64RS, pair->value, missing);
210 	}
211 
212 	/* Now turn off reporting features if any CPU is missing it. */
213 	pair->value &= ~missing;
214 }
215 
216 static bool hwprobe_ext0_has(const struct cpumask *cpus, u64 ext)
217 {
218 	struct riscv_hwprobe pair;
219 
220 	hwprobe_isa_ext0(&pair, cpus);
221 	return (pair.value & ext);
222 }
223 
224 #if defined(CONFIG_RISCV_PROBE_UNALIGNED_ACCESS)
225 static u64 hwprobe_misaligned(const struct cpumask *cpus)
226 {
227 	int cpu;
228 	u64 perf = -1ULL;
229 
230 	for_each_cpu(cpu, cpus) {
231 		int this_perf = per_cpu(misaligned_access_speed, cpu);
232 
233 		if (perf == -1ULL)
234 			perf = this_perf;
235 
236 		if (perf != this_perf) {
237 			perf = RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN;
238 			break;
239 		}
240 	}
241 
242 	if (perf == -1ULL)
243 		return RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN;
244 
245 	return perf;
246 }
247 #else
248 static u64 hwprobe_misaligned(const struct cpumask *cpus)
249 {
250 	if (IS_ENABLED(CONFIG_RISCV_EFFICIENT_UNALIGNED_ACCESS))
251 		return RISCV_HWPROBE_MISALIGNED_SCALAR_FAST;
252 
253 	if (IS_ENABLED(CONFIG_RISCV_EMULATED_UNALIGNED_ACCESS) && unaligned_ctl_available())
254 		return RISCV_HWPROBE_MISALIGNED_SCALAR_EMULATED;
255 
256 	return RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW;
257 }
258 #endif
259 
260 #ifdef CONFIG_RISCV_VECTOR_MISALIGNED
261 static u64 hwprobe_vec_misaligned(const struct cpumask *cpus)
262 {
263 	int cpu;
264 	u64 perf = -1ULL;
265 
266 	/* Return if supported or not even if speed wasn't probed */
267 	for_each_cpu(cpu, cpus) {
268 		int this_perf = per_cpu(vector_misaligned_access, cpu);
269 
270 		if (perf == -1ULL)
271 			perf = this_perf;
272 
273 		if (perf != this_perf) {
274 			perf = RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN;
275 			break;
276 		}
277 	}
278 
279 	if (perf == -1ULL)
280 		return RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN;
281 
282 	return perf;
283 }
284 #else
285 static u64 hwprobe_vec_misaligned(const struct cpumask *cpus)
286 {
287 	if (IS_ENABLED(CONFIG_RISCV_EFFICIENT_VECTOR_UNALIGNED_ACCESS))
288 		return RISCV_HWPROBE_MISALIGNED_VECTOR_FAST;
289 
290 	if (IS_ENABLED(CONFIG_RISCV_SLOW_VECTOR_UNALIGNED_ACCESS))
291 		return RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW;
292 
293 	return RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN;
294 }
295 #endif
296 
297 static void hwprobe_one_pair(struct riscv_hwprobe *pair,
298 			     const struct cpumask *cpus)
299 {
300 	pair->value = 0;
301 
302 	switch (pair->key) {
303 	case RISCV_HWPROBE_KEY_MVENDORID:
304 	case RISCV_HWPROBE_KEY_MARCHID:
305 	case RISCV_HWPROBE_KEY_MIMPID:
306 		hwprobe_arch_id(pair, cpus);
307 		break;
308 	/*
309 	 * The kernel already assumes that the base single-letter ISA
310 	 * extensions are supported on all harts, and only supports the
311 	 * IMA base, so just cheat a bit here and tell that to
312 	 * userspace.
313 	 */
314 	case RISCV_HWPROBE_KEY_BASE_BEHAVIOR:
315 		pair->value = RISCV_HWPROBE_BASE_BEHAVIOR_IMA;
316 		break;
317 
318 	case RISCV_HWPROBE_KEY_IMA_EXT_0:
319 		hwprobe_isa_ext0(pair, cpus);
320 		break;
321 
322 	case RISCV_HWPROBE_KEY_IMA_EXT_1:
323 		hwprobe_isa_ext1(pair, cpus);
324 		break;
325 
326 	case RISCV_HWPROBE_KEY_CPUPERF_0:
327 	case RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF:
328 		pair->value = hwprobe_misaligned(cpus);
329 		break;
330 
331 	case RISCV_HWPROBE_KEY_MISALIGNED_VECTOR_PERF:
332 		pair->value = hwprobe_vec_misaligned(cpus);
333 		break;
334 
335 	case RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE:
336 		if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOZ))
337 			pair->value = riscv_cboz_block_size;
338 		break;
339 	case RISCV_HWPROBE_KEY_ZICBOM_BLOCK_SIZE:
340 		if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOM))
341 			pair->value = riscv_cbom_block_size;
342 		break;
343 	case RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE:
344 		if (hwprobe_ext0_has(cpus, RISCV_HWPROBE_EXT_ZICBOP))
345 			pair->value = riscv_cbop_block_size;
346 		break;
347 	case RISCV_HWPROBE_KEY_HIGHEST_VIRT_ADDRESS:
348 		pair->value = user_max_virt_addr();
349 		break;
350 
351 	case RISCV_HWPROBE_KEY_TIME_CSR_FREQ:
352 		pair->value = riscv_timebase;
353 		break;
354 
355 	case RISCV_HWPROBE_KEY_VENDOR_EXT_SIFIVE_0:
356 		hwprobe_isa_vendor_ext_sifive_0(pair, cpus);
357 		break;
358 
359 	case RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0:
360 		hwprobe_isa_vendor_ext_thead_0(pair, cpus);
361 		break;
362 	case RISCV_HWPROBE_KEY_VENDOR_EXT_MIPS_0:
363 		hwprobe_isa_vendor_ext_mips_0(pair, cpus);
364 		break;
365 
366 	/*
367 	 * For forward compatibility, unknown keys don't fail the whole
368 	 * call, but get their element key set to -1 and value set to 0
369 	 * indicating they're unrecognized.
370 	 */
371 	default:
372 		pair->key = -1;
373 		pair->value = 0;
374 		break;
375 	}
376 }
377 
378 static int hwprobe_get_values(struct riscv_hwprobe __user *pairs,
379 			      size_t pair_count, size_t cpusetsize,
380 			      unsigned long __user *cpus_user,
381 			      unsigned int flags)
382 {
383 	size_t out;
384 	int ret;
385 	cpumask_t cpus;
386 
387 	/* Check the reserved flags. */
388 	if (flags != 0)
389 		return -EINVAL;
390 
391 	/*
392 	 * The interface supports taking in a CPU mask, and returns values that
393 	 * are consistent across that mask. Allow userspace to specify NULL and
394 	 * 0 as a shortcut to all online CPUs.
395 	 */
396 	cpumask_clear(&cpus);
397 	if (!cpusetsize && !cpus_user) {
398 		cpumask_copy(&cpus, cpu_online_mask);
399 	} else {
400 		if (cpusetsize > cpumask_size())
401 			cpusetsize = cpumask_size();
402 
403 		ret = copy_from_user(&cpus, cpus_user, cpusetsize);
404 		if (ret)
405 			return -EFAULT;
406 
407 		/*
408 		 * Userspace must provide at least one online CPU, without that
409 		 * there's no way to define what is supported.
410 		 */
411 		cpumask_and(&cpus, &cpus, cpu_online_mask);
412 		if (cpumask_empty(&cpus))
413 			return -EINVAL;
414 	}
415 
416 	for (out = 0; out < pair_count; out++, pairs++) {
417 		struct riscv_hwprobe pair;
418 
419 		if (get_user(pair.key, &pairs->key))
420 			return -EFAULT;
421 
422 		pair.value = 0;
423 		hwprobe_one_pair(&pair, &cpus);
424 		ret = put_user(pair.key, &pairs->key);
425 		if (ret == 0)
426 			ret = put_user(pair.value, &pairs->value);
427 
428 		if (ret)
429 			return -EFAULT;
430 	}
431 
432 	return 0;
433 }
434 
435 static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
436 			    size_t pair_count, size_t cpusetsize,
437 			    unsigned long __user *cpus_user,
438 			    unsigned int flags)
439 {
440 	cpumask_t cpus, one_cpu;
441 	bool clear_all = false;
442 	size_t i;
443 	int ret;
444 
445 	if (flags != RISCV_HWPROBE_WHICH_CPUS)
446 		return -EINVAL;
447 
448 	if (!cpusetsize || !cpus_user)
449 		return -EINVAL;
450 
451 	if (cpusetsize > cpumask_size())
452 		cpusetsize = cpumask_size();
453 
454 	cpumask_clear(&cpus);
455 	ret = copy_from_user(&cpus, cpus_user, cpusetsize);
456 	if (ret)
457 		return -EFAULT;
458 
459 	if (cpumask_empty(&cpus))
460 		cpumask_copy(&cpus, cpu_online_mask);
461 
462 	cpumask_and(&cpus, &cpus, cpu_online_mask);
463 
464 	cpumask_clear(&one_cpu);
465 
466 	for (i = 0; i < pair_count; i++) {
467 		struct riscv_hwprobe pair, tmp;
468 		int cpu;
469 
470 		ret = copy_from_user(&pair, &pairs[i], sizeof(pair));
471 		if (ret)
472 			return -EFAULT;
473 
474 		if (!riscv_hwprobe_key_is_valid(pair.key)) {
475 			clear_all = true;
476 			pair = (struct riscv_hwprobe){ .key = -1, };
477 			ret = copy_to_user(&pairs[i], &pair, sizeof(pair));
478 			if (ret)
479 				return -EFAULT;
480 		}
481 
482 		if (clear_all)
483 			continue;
484 
485 		tmp = (struct riscv_hwprobe){ .key = pair.key, };
486 
487 		for_each_cpu(cpu, &cpus) {
488 			cpumask_set_cpu(cpu, &one_cpu);
489 
490 			hwprobe_one_pair(&tmp, &one_cpu);
491 
492 			if (!riscv_hwprobe_pair_cmp(&tmp, &pair))
493 				cpumask_clear_cpu(cpu, &cpus);
494 
495 			cpumask_clear_cpu(cpu, &one_cpu);
496 		}
497 	}
498 
499 	if (clear_all)
500 		cpumask_clear(&cpus);
501 
502 	ret = copy_to_user(cpus_user, &cpus, cpusetsize);
503 	if (ret)
504 		return -EFAULT;
505 
506 	return 0;
507 }
508 
509 static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
510 			    size_t pair_count, size_t cpusetsize,
511 			    unsigned long __user *cpus_user,
512 			    unsigned int flags)
513 {
514 	if (flags & RISCV_HWPROBE_WHICH_CPUS)
515 		return hwprobe_get_cpus(pairs, pair_count, cpusetsize,
516 					cpus_user, flags);
517 
518 	return hwprobe_get_values(pairs, pair_count, cpusetsize,
519 				  cpus_user, flags);
520 }
521 
522 #ifdef CONFIG_MMU
523 
524 static int __init init_hwprobe_vdso_data(void)
525 {
526 	struct vdso_arch_data *avd = vdso_k_arch_data;
527 	u64 id_bitsmash = 0;
528 	struct riscv_hwprobe pair;
529 	int key;
530 
531 	/*
532 	 * Initialize vDSO data with the answers for the "all CPUs" case, to
533 	 * save a syscall in the common case.
534 	 */
535 	for (key = 0; key <= RISCV_HWPROBE_MAX_KEY; key++) {
536 		pair.key = key;
537 		hwprobe_one_pair(&pair, cpu_online_mask);
538 
539 		WARN_ON_ONCE(pair.key < 0);
540 
541 		avd->all_cpu_hwprobe_values[key] = pair.value;
542 		/*
543 		 * Smash together the vendor, arch, and impl IDs to see if
544 		 * they're all 0 or any negative.
545 		 */
546 		if (key <= RISCV_HWPROBE_KEY_MIMPID)
547 			id_bitsmash |= pair.value;
548 	}
549 
550 	/*
551 	 * If the arch, vendor, and implementation ID are all the same across
552 	 * all harts, then assume all CPUs are the same, and allow the vDSO to
553 	 * answer queries for arbitrary masks. However if all values are 0 (not
554 	 * populated) or any value returns -1 (varies across CPUs), then the
555 	 * vDSO should defer to the kernel for exotic cpu masks.
556 	 */
557 	avd->homogeneous_cpus = id_bitsmash != 0 && id_bitsmash != -1;
558 	return 0;
559 }
560 
561 arch_initcall_sync(init_hwprobe_vdso_data);
562 
563 #endif /* CONFIG_MMU */
564 
565 SYSCALL_DEFINE5(riscv_hwprobe, struct riscv_hwprobe __user *, pairs,
566 		size_t, pair_count, size_t, cpusetsize, unsigned long __user *,
567 		cpus, unsigned int, flags)
568 {
569 	return do_riscv_hwprobe(pairs, pair_count, cpusetsize,
570 				cpus, flags);
571 }
572