xref: /linux/arch/riscv/kernel/vdso/hwprobe.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2023 Rivos, Inc
4  */
5 
6 #include <linux/string.h>
7 #include <linux/types.h>
8 #include <vdso/datapage.h>
9 #include <vdso/helpers.h>
10 
11 extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
12 			 size_t cpusetsize, unsigned long *cpus,
13 			 unsigned int flags);
14 
15 static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
16 				 size_t cpusetsize, unsigned long *cpus,
17 				 unsigned int flags)
18 {
19 	const struct vdso_data *vd = __arch_get_vdso_data();
20 	const struct arch_vdso_data *avd = &vd->arch_data;
21 	bool all_cpus = !cpusetsize && !cpus;
22 	struct riscv_hwprobe *p = pairs;
23 	struct riscv_hwprobe *end = pairs + pair_count;
24 
25 	/*
26 	 * Defer to the syscall for exotic requests. The vdso has answers
27 	 * stashed away only for the "all cpus" case. If all CPUs are
28 	 * homogeneous, then this function can handle requests for arbitrary
29 	 * masks.
30 	 */
31 	if ((flags != 0) || (!all_cpus && !avd->homogeneous_cpus))
32 		return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
33 
34 	/* This is something we can handle, fill out the pairs. */
35 	while (p < end) {
36 		if (riscv_hwprobe_key_is_valid(p->key)) {
37 			p->value = avd->all_cpu_hwprobe_values[p->key];
38 
39 		} else {
40 			p->key = -1;
41 			p->value = 0;
42 		}
43 
44 		p++;
45 	}
46 
47 	return 0;
48 }
49 
50 static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
51 			       size_t cpusetsize, unsigned long *cpus,
52 			       unsigned int flags)
53 {
54 	const struct vdso_data *vd = __arch_get_vdso_data();
55 	const struct arch_vdso_data *avd = &vd->arch_data;
56 	struct riscv_hwprobe *p = pairs;
57 	struct riscv_hwprobe *end = pairs + pair_count;
58 	unsigned char *c = (unsigned char *)cpus;
59 	bool empty_cpus = true;
60 	bool clear_all = false;
61 	int i;
62 
63 	if (!cpusetsize || !cpus)
64 		return -EINVAL;
65 
66 	for (i = 0; i < cpusetsize; i++) {
67 		if (c[i]) {
68 			empty_cpus = false;
69 			break;
70 		}
71 	}
72 
73 	if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS || !avd->homogeneous_cpus)
74 		return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
75 
76 	while (p < end) {
77 		if (riscv_hwprobe_key_is_valid(p->key)) {
78 			struct riscv_hwprobe t = {
79 				.key = p->key,
80 				.value = avd->all_cpu_hwprobe_values[p->key],
81 			};
82 
83 			if (!riscv_hwprobe_pair_cmp(&t, p))
84 				clear_all = true;
85 		} else {
86 			clear_all = true;
87 			p->key = -1;
88 			p->value = 0;
89 		}
90 		p++;
91 	}
92 
93 	if (clear_all) {
94 		for (i = 0; i < cpusetsize; i++)
95 			c[i] = 0;
96 	}
97 
98 	return 0;
99 }
100 
101 /* Add a prototype to avoid -Wmissing-prototypes warning. */
102 int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
103 			 size_t cpusetsize, unsigned long *cpus,
104 			 unsigned int flags);
105 
106 int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
107 			 size_t cpusetsize, unsigned long *cpus,
108 			 unsigned int flags)
109 {
110 	if (flags & RISCV_HWPROBE_WHICH_CPUS)
111 		return riscv_vdso_get_cpus(pairs, pair_count, cpusetsize,
112 					   cpus, flags);
113 
114 	return riscv_vdso_get_values(pairs, pair_count, cpusetsize,
115 				     cpus, flags);
116 }
117