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
11extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
12			 size_t cpusetsize, unsigned long *cpus,
13			 unsigned int flags);
14
15static 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
50static 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. */
102int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
103			 size_t cpusetsize, unsigned long *cpus,
104			 unsigned int flags);
105
106int __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