|
| 1 | +//=== ifunc_select.c - Check environment hardware feature -*- C -*-===========// |
| 2 | +// |
| 3 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | +// See https://llvm.org/LICENSE.txt for license information. |
| 5 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | +// |
| 7 | +//===----------------------------------------------------------------------===// |
| 8 | + |
| 9 | +#define BUFSIZE 1024 |
| 10 | + |
| 11 | +static void fillzero(char *s, unsigned size) { |
| 12 | + for (int i = 0; i < size; i++) |
| 13 | + s[i] = '\0'; |
| 14 | +} |
| 15 | + |
| 16 | +static unsigned strcmp(const char *a, const char *b) { |
| 17 | + while (*a != '\0' && *b != '\0') { |
| 18 | + if (*a != *b) |
| 19 | + return 0; |
| 20 | + a++; |
| 21 | + b++; |
| 22 | + } |
| 23 | + if (*a == *b) |
| 24 | + return 1; |
| 25 | + return 0; |
| 26 | +} |
| 27 | + |
| 28 | +static void memcpy(char *dest, const char *src, unsigned len) { |
| 29 | + for (unsigned i = 0; i < len; i++) |
| 30 | + dest[i] = src[i]; |
| 31 | +} |
| 32 | + |
| 33 | +static unsigned strlen(char *s) { |
| 34 | + int len = 0; |
| 35 | + while (*s != '\0') { |
| 36 | + len++; |
| 37 | + s++; |
| 38 | + } |
| 39 | + return len; |
| 40 | +} |
| 41 | + |
| 42 | +// TODO: Replace with more efficient algorithm |
| 43 | +static unsigned isPatternInSrc(char *src, char *pattern) { |
| 44 | + char buf[BUFSIZE]; |
| 45 | + int lenOfSrc = strlen(src); |
| 46 | + int lenOfPattern = strlen(pattern); |
| 47 | + for (int i = 0; i + lenOfPattern < lenOfSrc; i++) { |
| 48 | + fillzero(buf, BUFSIZE); |
| 49 | + memcpy(buf, src + i, lenOfPattern); |
| 50 | + if (strcmp(buf, pattern)) |
| 51 | + return 1; |
| 52 | + } |
| 53 | + return 0; |
| 54 | +} |
| 55 | + |
| 56 | +static char *tokenize(char *src, char *rst, char delim) { |
| 57 | + while (*src != '\0' && *src != delim) { |
| 58 | + *rst = *src; |
| 59 | + src++; |
| 60 | + rst++; |
| 61 | + } |
| 62 | + if (*src == delim) |
| 63 | + src++; |
| 64 | + return src; |
| 65 | +} |
| 66 | + |
| 67 | +static void getBasicExtFromHwFeatStr(char *rst, char *src) { |
| 68 | + while (*src != 'r') { |
| 69 | + src++; |
| 70 | + } |
| 71 | + |
| 72 | + while (*src != '_' && *src != '\0') { |
| 73 | + *rst = *src; |
| 74 | + rst++; |
| 75 | + src++; |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +static unsigned checkFeatStrInHwFeatStr(char *FeatStr, char *HwFeatStr) { |
| 80 | + // For basic extension like a,m,f,d... |
| 81 | + if (strlen(FeatStr) == 1) { |
| 82 | + char BasicExt[BUFSIZE]; |
| 83 | + fillzero(BasicExt, BUFSIZE); |
| 84 | + getBasicExtFromHwFeatStr(BasicExt, HwFeatStr); |
| 85 | + return isPatternInSrc(BasicExt, FeatStr); |
| 86 | + } |
| 87 | + // For zbb,zihintntl... |
| 88 | + return isPatternInSrc(HwFeatStr, FeatStr); |
| 89 | +} |
| 90 | + |
| 91 | +#if defined(__linux__) |
| 92 | + |
| 93 | +#define SYS_read 63 |
| 94 | +#define SYS_openat 56 |
| 95 | +#define SYS_riscv_hwprobe 258 |
| 96 | + |
| 97 | +static long syscall_impl_3_args(long number, long arg1, long arg2, long arg3) { |
| 98 | + register long a7 __asm__("a7") = number; |
| 99 | + register long a0 __asm__("a0") = arg1; |
| 100 | + register long a1 __asm__("a1") = arg2; |
| 101 | + register long a2 __asm__("a2") = arg3; |
| 102 | + __asm__ __volatile__("ecall\n\t" |
| 103 | + : "=r"(a0) |
| 104 | + : "r"(a7), "r"(a0), "r"(a1), "r"(a2) |
| 105 | + : "memory"); |
| 106 | + return a0; |
| 107 | +} |
| 108 | + |
| 109 | +static long syscall_impl_5_args(long number, long arg1, long arg2, long arg3, |
| 110 | + long arg4, long arg5) { |
| 111 | + register long a7 __asm__("a7") = number; |
| 112 | + register long a0 __asm__("a0") = arg1; |
| 113 | + register long a1 __asm__("a1") = arg2; |
| 114 | + register long a2 __asm__("a2") = arg3; |
| 115 | + register long a3 __asm__("a3") = arg4; |
| 116 | + register long a4 __asm__("a4") = arg5; |
| 117 | + __asm__ __volatile__("ecall\n\t" |
| 118 | + : "=r"(a0) |
| 119 | + : "r"(a7), "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4) |
| 120 | + : "memory"); |
| 121 | + return a0; |
| 122 | +} |
| 123 | + |
| 124 | +static unsigned read(int fd, const void *buf, unsigned count) { |
| 125 | + return syscall_impl_3_args(SYS_read, fd, (long)buf, count); |
| 126 | +} |
| 127 | + |
| 128 | +static int openat(int dirfd, const char *pathname, int flags) { |
| 129 | + return syscall_impl_3_args(SYS_openat, (long)dirfd, (long)pathname, flags); |
| 130 | +} |
| 131 | + |
| 132 | +static void readUntilNextLine(int fd, char *rst, int len) { |
| 133 | + char buf = '\0'; |
| 134 | + while (buf != '\n' && read(fd, &buf, 1) != 0 && len > 0) { |
| 135 | + *rst = buf; |
| 136 | + rst++; |
| 137 | + len--; |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +static void __riscv_cpuinfo(char *FeatStr) { |
| 142 | + char buf[BUFSIZE]; |
| 143 | + int fd = openat(0, "/proc/cpuinfo", 2); |
| 144 | + do { |
| 145 | + fillzero(buf, BUFSIZE); |
| 146 | + readUntilNextLine(fd, buf, BUFSIZE); |
| 147 | + if (isPatternInSrc(buf, "isa")) { |
| 148 | + memcpy(FeatStr, buf, BUFSIZE); |
| 149 | + return; |
| 150 | + } |
| 151 | + } while (strlen(buf) != 0); |
| 152 | +} |
| 153 | + |
| 154 | +struct riscv_hwprobe { |
| 155 | + long long key; |
| 156 | + unsigned long long value; |
| 157 | +}; |
| 158 | + |
| 159 | +#define RISCV_HWPROBE_MAX_KEY 5 |
| 160 | +#define RISCV_HWPROBE_KEY_MVENDORID 0 |
| 161 | +#define RISCV_HWPROBE_KEY_MARCHID 1 |
| 162 | +#define RISCV_HWPROBE_KEY_MIMPID 2 |
| 163 | +#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3 |
| 164 | +#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0) |
| 165 | +#define RISCV_HWPROBE_KEY_IMA_EXT_0 4 |
| 166 | +#define RISCV_HWPROBE_IMA_FD (1 << 0) |
| 167 | +#define RISCV_HWPROBE_IMA_C (1 << 1) |
| 168 | +#define RISCV_HWPROBE_IMA_V (1 << 2) |
| 169 | +#define RISCV_HWPROBE_EXT_ZBA (1 << 3) |
| 170 | +#define RISCV_HWPROBE_EXT_ZBB (1 << 4) |
| 171 | +#define RISCV_HWPROBE_EXT_ZBS (1 << 5) |
| 172 | +#define RISCV_HWPROBE_KEY_CPUPERF_0 5 |
| 173 | +#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0) |
| 174 | +#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0) |
| 175 | +#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0) |
| 176 | +#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0) |
| 177 | +#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0) |
| 178 | +#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0) |
| 179 | + |
| 180 | +/* Size definition for CPU sets. */ |
| 181 | +#define __CPU_SETSIZE 1024 |
| 182 | +#define __NCPUBITS (8 * sizeof(unsigned long int)) |
| 183 | + |
| 184 | +/* Data structure to describe CPU mask. */ |
| 185 | +typedef struct { |
| 186 | + unsigned long int __bits[__CPU_SETSIZE / __NCPUBITS]; |
| 187 | +} cpu_set_t; |
| 188 | + |
| 189 | +static long sys_riscv_hwprobe(struct riscv_hwprobe *pairs, unsigned pair_count, |
| 190 | + unsigned cpu_count, cpu_set_t *cpus, |
| 191 | + unsigned int flags) { |
| 192 | + return syscall_impl_5_args(SYS_riscv_hwprobe, (long)pairs, pair_count, |
| 193 | + cpu_count, (long)cpus, flags); |
| 194 | +} |
| 195 | + |
| 196 | +static void initHwProbe(struct riscv_hwprobe *Hwprobes, int len) { |
| 197 | + sys_riscv_hwprobe(Hwprobes, len, 0, 0, 0); |
| 198 | +} |
| 199 | + |
| 200 | +static unsigned checkFeatStrInHwProbe(char *FeatStr, struct riscv_hwprobe Hwprobe) { |
| 201 | + if (Hwprobe.key == -1) |
| 202 | + return 0; |
| 203 | + |
| 204 | + if (strcmp(FeatStr, "i")) |
| 205 | + return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0; |
| 206 | + if (strcmp(FeatStr, "m")) |
| 207 | + return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0; |
| 208 | + if (strcmp(FeatStr, "a")) |
| 209 | + return Hwprobe.value & RISCV_HWPROBE_KEY_IMA_EXT_0; |
| 210 | + if (strcmp(FeatStr, "f")) |
| 211 | + return Hwprobe.value & RISCV_HWPROBE_IMA_FD; |
| 212 | + if (strcmp(FeatStr, "d")) |
| 213 | + return Hwprobe.value & RISCV_HWPROBE_IMA_FD; |
| 214 | + if (strcmp(FeatStr, "c")) |
| 215 | + return Hwprobe.value & RISCV_HWPROBE_IMA_C; |
| 216 | + if (strcmp(FeatStr, "v")) |
| 217 | + return Hwprobe.value & RISCV_HWPROBE_IMA_V; |
| 218 | + if (strcmp(FeatStr, "zba")) |
| 219 | + return Hwprobe.value & RISCV_HWPROBE_EXT_ZBA; |
| 220 | + if (strcmp(FeatStr, "zbb")) |
| 221 | + return Hwprobe.value & RISCV_HWPROBE_EXT_ZBB; |
| 222 | + if (strcmp(FeatStr, "zbs")) |
| 223 | + return Hwprobe.value & RISCV_HWPROBE_EXT_ZBS; |
| 224 | + |
| 225 | + return 0; |
| 226 | +} |
| 227 | +#endif // defined(__linux__) |
| 228 | + |
| 229 | +// FeatStr format is like <Feature1>;<Feature2>...<FeatureN>. |
| 230 | +unsigned __riscv_ifunc_select(char *FeatStrs) { |
| 231 | +#if defined(__linux__) |
| 232 | + // Init Hwprobe |
| 233 | + struct riscv_hwprobe pairs[] = { |
| 234 | + {RISCV_HWPROBE_KEY_IMA_EXT_0, 0}, |
| 235 | + }; |
| 236 | + initHwProbe(pairs, 1); |
| 237 | + // Init from cpuinfo |
| 238 | + char HwFeatStr[BUFSIZE]; |
| 239 | + fillzero(HwFeatStr, BUFSIZE); |
| 240 | + __riscv_cpuinfo(HwFeatStr); |
| 241 | + |
| 242 | + // Check each extension whether available |
| 243 | + char FeatStr[BUFSIZE]; |
| 244 | + while (*FeatStrs != '\0') { |
| 245 | + fillzero(FeatStr, BUFSIZE); |
| 246 | + FeatStrs = tokenize(FeatStrs, FeatStr, ';'); |
| 247 | + if (checkFeatStrInHwProbe(FeatStr, pairs[0])) |
| 248 | + continue; |
| 249 | + if (!checkFeatStrInHwFeatStr(FeatStr, HwFeatStr)) |
| 250 | + return 0; |
| 251 | + } |
| 252 | + |
| 253 | + return 1; |
| 254 | +#else |
| 255 | + // If other platform support IFUNC, need to implement its |
| 256 | + // __riscv_ifunc_select. |
| 257 | + return 0; |
| 258 | +#endif |
| 259 | +} |
0 commit comments