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 
riscv_vdso_get_values(struct riscv_hwprobe * pairs,size_t pair_count,size_t cpusetsize,unsigned long * cpus,unsigned int flags)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_arch_data *avd = &vdso_u_arch_data;
20 	bool all_cpus = !cpusetsize && !cpus;
21 	struct riscv_hwprobe *p = pairs;
22 	struct riscv_hwprobe *end = pairs + pair_count;
23 
24 	/*
25 	 * Defer to the syscall for exotic requests. The vdso has answers
26 	 * stashed away only for the "all cpus" case. If all CPUs are
27 	 * homogeneous, then this function can handle requests for arbitrary
28 	 * masks.
29 	 */
30 	if ((flags != 0) || (!all_cpus && !avd->homogeneous_cpus))
31 		return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
32 
33 	/* This is something we can handle, fill out the pairs. */
34 	while (p < end) {
35 		if (riscv_hwprobe_key_is_valid(p->key)) {
36 			p->value = avd->all_cpu_hwprobe_values[p->key];
37 
38 		} else {
39 			p->key = -1;
40 			p->value = 0;
41 		}
42 
43 		p++;
44 	}
45 
46 	return 0;
47 }
48 
riscv_vdso_get_cpus(struct riscv_hwprobe * pairs,size_t pair_count,size_t cpusetsize,unsigned long * cpus,unsigned int flags)49 static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count,
50 			       size_t cpusetsize, unsigned long *cpus,
51 			       unsigned int flags)
52 {
53 	const struct vdso_arch_data *avd = &vdso_u_arch_data;
54 	struct riscv_hwprobe *p = pairs;
55 	struct riscv_hwprobe *end = pairs + pair_count;
56 	unsigned char *c = (unsigned char *)cpus;
57 	bool empty_cpus = true;
58 	bool clear_all = false;
59 	int i;
60 
61 	if (!cpusetsize || !cpus)
62 		return -EINVAL;
63 
64 	for (i = 0; i < cpusetsize; i++) {
65 		if (c[i]) {
66 			empty_cpus = false;
67 			break;
68 		}
69 	}
70 
71 	if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS || !avd->homogeneous_cpus)
72 		return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
73 
74 	while (p < end) {
75 		if (riscv_hwprobe_key_is_valid(p->key)) {
76 			struct riscv_hwprobe t = {
77 				.key = p->key,
78 				.value = avd->all_cpu_hwprobe_values[p->key],
79 			};
80 
81 			if (!riscv_hwprobe_pair_cmp(&t, p))
82 				clear_all = true;
83 		} else {
84 			clear_all = true;
85 			p->key = -1;
86 			p->value = 0;
87 		}
88 		p++;
89 	}
90 
91 	if (clear_all) {
92 		for (i = 0; i < cpusetsize; i++)
93 			c[i] = 0;
94 	}
95 
96 	return 0;
97 }
98 
99 /* Add a prototype to avoid -Wmissing-prototypes warning. */
100 int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
101 			 size_t cpusetsize, unsigned long *cpus,
102 			 unsigned int flags);
103 
__vdso_riscv_hwprobe(struct riscv_hwprobe * pairs,size_t pair_count,size_t cpusetsize,unsigned long * cpus,unsigned int flags)104 int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
105 			 size_t cpusetsize, unsigned long *cpus,
106 			 unsigned int flags)
107 {
108 	if (flags & RISCV_HWPROBE_WHICH_CPUS)
109 		return riscv_vdso_get_cpus(pairs, pair_count, cpusetsize,
110 					   cpus, flags);
111 
112 	return riscv_vdso_get_values(pairs, pair_count, cpusetsize,
113 				     cpus, flags);
114 }
115