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_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 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 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