FFmpeg
cpu.c
Go to the documentation of this file.
1 /*
2  * Copyright © 2022-2025 Rémi Denis-Courmont.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "checkasm_config.h"
29 
30 #if ARCH_RISCV
31 
32  #ifndef _GNU_SOURCE
33  #define _GNU_SOURCE
34  #endif
35 
36  #include "cpu.h"
37  #include "internal.h"
38 
39  #include <inttypes.h>
40  #include <limits.h>
41  #include <stdatomic.h>
42  #include <stdbool.h>
43  #if HAVE_SYS_HWPROBE_H
44  #include <sys/hwprobe.h>
45  #elif HAVE_ASM_HWPROBE_H
46  #include <asm/hwprobe.h>
47  #include <sys/syscall.h>
48  #include <unistd.h>
49 
50 static int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
51  size_t cpu_count, unsigned long *cpus,
52  unsigned int flags)
53 {
54  return syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_count, cpus,
55  flags);
56 }
57  #endif
58  #if HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
59  #include <sys/auxv.h>
60  #define HWCAP_RV(letter) (1ul << ((letter) - 'A'))
61  #endif
62 
63 int checkasm_get_cpuids(uint32_t *vendor, uintptr_t *arch, uintptr_t *imp)
64 {
65 #if HAVE_SYS_HWPROBE_H || HAVE_ASM_HWPROBE_H
66  struct riscv_hwprobe pairs[] = {
67  { RISCV_HWPROBE_KEY_MVENDORID, 0 },
68  { RISCV_HWPROBE_KEY_MARCHID, 0 },
69  { RISCV_HWPROBE_KEY_MIMPID, 0 },
70  };
71 
72  if (__riscv_hwprobe(pairs, ARRAY_SIZE(pairs), 0, NULL, 0) == 0) {
73  *vendor = (uint32_t)pairs[0].value;
74  *arch = (uintptr_t)pairs[1].value;
75  *imp = (uintptr_t)pairs[2].value;
76  return 0;
77  }
78 #endif
79  return -1;
80 }
81 
82 const char *checkasm_get_riscv_vendor_name(uint32_t vendorid)
83 {
84  if (vendorid > 0) {
85  unsigned bank = vendorid >> 7, offset = vendorid & 0x7f;
86 
88  } else {
89  return "Unspecified";
90  }
91 }
92 
93 const char *checkasm_get_riscv_arch_name(char *buf, size_t buflen,
94  uint32_t vendor, uintptr_t arch)
95 {
96  if (arch & INTPTR_MIN) { // Proprietary vendor-specific architecture
97  arch &= INTPTR_MAX;
98  snprintf(buf, buflen, "vendor arch 0x%"PRIXPTR, arch);
99  return buf;
100 
101  } else {
102  switch (arch) { // Open core listed in ISA manual.
103  case 0: return "Unspecified";
104  case 5: return "Spike";
105  case 42: return "QEMU";
106  default:
107  snprintf(buf, buflen, "open arch %"PRIuPTR, arch);
108  return buf;
109  }
110  }
111 }
112 
113 /* CPU capabilities relevant to the checked call harness. */
114 #define RISCV_FLOAT (1 << 0)
115 #define RISCV_VECTOR (1 << 1)
116 
117 static int checkasm_hwprobe(void)
118 {
119  int flags = 0;
120 #if HAVE_SYS_HWPROBE_H || HAVE_ASM_HWPROBE_H
121  struct riscv_hwprobe pairs[] = {
122  { RISCV_HWPROBE_KEY_IMA_EXT_0, 0 },
123  };
124 
125  if (__riscv_hwprobe(pairs, ARRAY_SIZE(pairs), 0, NULL, 0) == 0) {
126  if (pairs[0].value & RISCV_HWPROBE_IMA_FD)
127  flags |= RISCV_FLOAT;
128 #ifdef RISCV_HWPROBE_EXT_ZVE32X
129  if (pairs[0].value & RISCV_HWPROBE_EXT_ZVE32X)
130  flags |= RISCV_VECTOR;
131 #endif
132 #ifdef RISCV_HWPROBE_IMA_V
133  // A few Linux kernel (6.6+) versions recognise V but not Zve32x.
134  if (pairs[0].value & RISCV_HWPROBE_IMA_V)
135  flags |= RISCV_VECTOR;
136 #endif
137  }
138  /*
139  * We purposely do not fallback to HWCAP on Linux. Kernel versions without
140  * `hwprobe()` have hard-coded float support (on or off) and do not support
141  * other register extensions such as vectors.
142  */
143 #elif HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO
144  {
145  const unsigned long hwcap = checkasm_getauxval(AT_HWCAP);
146 
147  if (hwcap & HWCAP_RV('F'))
148  flags |= RISCV_FLOAT;
149  if (hwcap & HWCAP_RV('V'))
150  flags |= RISCV_VECTOR;
151  }
152 #endif
153 #ifdef __riscv_f
154  flags |= RISCV_FLOAT;
155 #endif
156 #ifdef __riscv_vector
157  flags |= RISCV_VECTOR;
158 #endif
159  return flags;
160 }
161 
162 static int checkasm_cpu_flags(void)
163 {
164  static atomic_int cpu_flags = INT_MIN;
165  int flags = atomic_load_explicit(&cpu_flags, memory_order_relaxed);
166 
167  if (flags < 0) {
168  flags = checkasm_hwprobe();
169  atomic_store_explicit(&cpu_flags, flags, memory_order_relaxed);
170  }
171 
172  return flags;
173 }
174 
175 int checkasm_has_float(void)
176 {
177  return (checkasm_cpu_flags() & RISCV_FLOAT) != 0;
178 }
179 
180 int checkasm_has_vector(void)
181 {
182  return (checkasm_cpu_flags() & RISCV_VECTOR) != 0;
183 }
184 
185 void *checkasm_checked_call_ptr(void)
186 {
187  void *checked_call = NULL;
188  int flags = checkasm_cpu_flags();
189 
190  switch (flags) {
191 #ifdef __riscv_float_abi_soft
192  case 0:
193  checked_call = checkasm_checked_call_i;
194  break;
195  case RISCV_VECTOR:
196  checked_call = checkasm_checked_call_iv;
197  break;
198 #endif
199  case RISCV_FLOAT:
200  checked_call = checkasm_checked_call_if;
201  break;
202  case RISCV_FLOAT | RISCV_VECTOR:
203  checked_call = checkasm_checked_call_ifv;
204  break;
205  }
206  assert(checked_call != NULL);
207  return checked_call;
208 }
209 
210 #endif /* ARCH_RISCV */
flags
const SwsFlags flags[]
Definition: swscale.c:85
checkasm_config.h
atomic_int
intptr_t atomic_int
Definition: stdatomic.h:55
checkasm_getauxval
COLD unsigned long checkasm_getauxval(unsigned long type)
Definition: cpu.c:51
checkasm_checked_call_ptr
CHECKASM_API checkasm_checked_call_func checkasm_checked_call_ptr(void)
limits.h
AT_HWCAP
#define AT_HWCAP
Definition: cpu.c:50
NULL
#define NULL
Definition: coverity.c:32
cpu_count
static atomic_int cpu_count
Definition: cpu.c:57
atomic_load_explicit
#define atomic_load_explicit(object, order)
Definition: stdatomic.h:96
ARRAY_SIZE
#define ARRAY_SIZE(a)
Definition: internal.h:81
cpu_flags
CheckasmCpu cpu_flags
Definition: checkasm.c:84
checkasm_get_jedec_vendor_name
const COLD char * checkasm_get_jedec_vendor_name(unsigned bank, unsigned offset)
Definition: cpu.c:159
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
atomic_store_explicit
#define atomic_store_explicit(object, desired, order)
Definition: stdatomic.h:90
value
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default value
Definition: writing_filters.txt:86
snprintf
#define snprintf
Definition: snprintf.h:34