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_data *vd = __arch_get_vdso_data();
20 const struct arch_vdso_time_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
riscv_vdso_get_cpus(struct riscv_hwprobe * pairs,size_t pair_count,size_t cpusetsize,unsigned long * cpus,unsigned int flags)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_time_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
__vdso_riscv_hwprobe(struct riscv_hwprobe * pairs,size_t pair_count,size_t cpusetsize,unsigned long * cpus,unsigned int flags)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