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