GNU Linux-libre 6.8.7-gnu
[releases.git] / arch / riscv / kernel / vdso / hwprobe.c
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 }