| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright 2023 Rivos, Inc | |
| */ | |
| #include <linux/string.h> | |
| #include <linux/types.h> | |
| #include <vdso/datapage.h> | |
| #include <vdso/helpers.h> | |
| extern int riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, | |
| size_t cpusetsize, unsigned long *cpus, | |
| unsigned int flags); | |
| static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count, | |
| size_t cpusetsize, unsigned long *cpus, | |
| unsigned int flags) | |
| { | |
| const struct vdso_data *vd = __arch_get_vdso_data(); | |
| const struct arch_vdso_data *avd = &vd->arch_data; | |
| bool all_cpus = !cpusetsize && !cpus; | |
| struct riscv_hwprobe *p = pairs; | |
| struct riscv_hwprobe *end = pairs + pair_count; | |
| /* | |
| * Defer to the syscall for exotic requests. The vdso has answers | |
| * stashed away only for the "all cpus" case. If all CPUs are | |
| * homogeneous, then this function can handle requests for arbitrary | |
| * masks. | |
| */ | |
| if ((flags != 0) || (!all_cpus && !avd->homogeneous_cpus)) | |
| return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags); | |
| /* This is something we can handle, fill out the pairs. */ | |
| while (p < end) { | |
| if (riscv_hwprobe_key_is_valid(p->key)) { | |
| p->value = avd->all_cpu_hwprobe_values[p->key]; | |
| } else { | |
| p->key = -1; | |
| p->value = 0; | |
| } | |
| p++; | |
| } | |
| return 0; | |
| } | |
| static int riscv_vdso_get_cpus(struct riscv_hwprobe *pairs, size_t pair_count, | |
| size_t cpusetsize, unsigned long *cpus, | |
| unsigned int flags) | |
| { | |
| const struct vdso_data *vd = __arch_get_vdso_data(); | |
| const struct arch_vdso_data *avd = &vd->arch_data; | |
| struct riscv_hwprobe *p = pairs; | |
| struct riscv_hwprobe *end = pairs + pair_count; | |
| unsigned char *c = (unsigned char *)cpus; | |
| bool empty_cpus = true; | |
| bool clear_all = false; | |
| int i; | |
| if (!cpusetsize || !cpus) | |
| return -EINVAL; | |
| for (i = 0; i < cpusetsize; i++) { | |
| if (c[i]) { | |
| empty_cpus = false; | |
| break; | |
| } | |
| } | |
| if (empty_cpus || flags != RISCV_HWPROBE_WHICH_CPUS || !avd->homogeneous_cpus) | |
| return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags); | |
| while (p < end) { | |
| if (riscv_hwprobe_key_is_valid(p->key)) { | |
| struct riscv_hwprobe t = { | |
| .key = p->key, | |
| .value = avd->all_cpu_hwprobe_values[p->key], | |
| }; | |
| if (!riscv_hwprobe_pair_cmp(&t, p)) | |
| clear_all = true; | |
| } else { | |
| clear_all = true; | |
| p->key = -1; | |
| p->value = 0; | |
| } | |
| p++; | |
| } | |
| if (clear_all) { | |
| for (i = 0; i < cpusetsize; i++) | |
| c[i] = 0; | |
| } | |
| return 0; | |
| } | |
| /* Add a prototype to avoid -Wmissing-prototypes warning. */ | |
| int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, | |
| size_t cpusetsize, unsigned long *cpus, | |
| unsigned int flags); | |
| int __vdso_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, | |
| size_t cpusetsize, unsigned long *cpus, | |
| unsigned int flags) | |
| { | |
| if (flags & RISCV_HWPROBE_WHICH_CPUS) | |
| return riscv_vdso_get_cpus(pairs, pair_count, cpusetsize, | |
| cpus, flags); | |
| return riscv_vdso_get_values(pairs, pair_count, cpusetsize, | |
| cpus, flags); | |
| } |