1 #include <stdbool.h>
2 #include <stddef.h>
3 
4 #include <cpuinfo.h>
5 #include <cpuinfo/internal-api.h>
6 #include <cpuinfo/log.h>
7 
8 #ifdef __linux__
9 	#include <linux/api.h>
10 
11 	#include <unistd.h>
12 	#include <sys/syscall.h>
13 	#if !defined(__NR_getcpu)
14 		#include <asm-generic/unistd.h>
15 	#endif
16 #endif
17 
18 bool cpuinfo_is_initialized = false;
19 
20 struct cpuinfo_processor* cpuinfo_processors = NULL;
21 struct cpuinfo_core* cpuinfo_cores = NULL;
22 struct cpuinfo_cluster* cpuinfo_clusters = NULL;
23 struct cpuinfo_package* cpuinfo_packages = NULL;
24 struct cpuinfo_cache* cpuinfo_cache[cpuinfo_cache_level_max] = { NULL };
25 
26 uint32_t cpuinfo_processors_count = 0;
27 uint32_t cpuinfo_cores_count = 0;
28 uint32_t cpuinfo_clusters_count = 0;
29 uint32_t cpuinfo_packages_count = 0;
30 uint32_t cpuinfo_cache_count[cpuinfo_cache_level_max] = { 0 };
31 uint32_t cpuinfo_max_cache_size = 0;
32 
33 #if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
34 	struct cpuinfo_uarch_info* cpuinfo_uarchs = NULL;
35 	uint32_t cpuinfo_uarchs_count = 0;
36 #else
37 	struct cpuinfo_uarch_info cpuinfo_global_uarch = { cpuinfo_uarch_unknown };
38 #endif
39 
40 #ifdef __linux__
41 	uint32_t cpuinfo_linux_cpu_max = 0;
42 	const struct cpuinfo_processor** cpuinfo_linux_cpu_to_processor_map = NULL;
43 	const struct cpuinfo_core** cpuinfo_linux_cpu_to_core_map = NULL;
44 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
45 		const uint32_t* cpuinfo_linux_cpu_to_uarch_index_map = NULL;
46 	#endif
47 #endif
48 
49 
cpuinfo_get_processors(void)50 const struct cpuinfo_processor* cpuinfo_get_processors(void) {
51 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
52 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors");
53 	}
54 	return cpuinfo_processors;
55 }
56 
cpuinfo_get_cores(void)57 const struct cpuinfo_core* cpuinfo_get_cores(void) {
58 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
59 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
60 	}
61 	return cpuinfo_cores;
62 }
63 
cpuinfo_get_clusters(void)64 const struct cpuinfo_cluster* cpuinfo_get_clusters(void) {
65 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
66 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters");
67 	}
68 	return cpuinfo_clusters;
69 }
70 
cpuinfo_get_packages(void)71 const struct cpuinfo_package* cpuinfo_get_packages(void) {
72 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
73 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages");
74 	}
75 	return cpuinfo_packages;
76 }
77 
cpuinfo_get_uarchs()78 const struct cpuinfo_uarch_info* cpuinfo_get_uarchs() {
79 	if (!cpuinfo_is_initialized) {
80 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs");
81 	}
82 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
83 		return cpuinfo_uarchs;
84 	#else
85 		return &cpuinfo_global_uarch;
86 	#endif
87 }
88 
cpuinfo_get_processor(uint32_t index)89 const struct cpuinfo_processor* cpuinfo_get_processor(uint32_t index) {
90 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
91 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processor");
92 	}
93 	if CPUINFO_UNLIKELY(index >= cpuinfo_processors_count) {
94 		return NULL;
95 	}
96 	return &cpuinfo_processors[index];
97 }
98 
cpuinfo_get_core(uint32_t index)99 const struct cpuinfo_core* cpuinfo_get_core(uint32_t index) {
100 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
101 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "core");
102 	}
103 	if CPUINFO_UNLIKELY(index >= cpuinfo_cores_count) {
104 		return NULL;
105 	}
106 	return &cpuinfo_cores[index];
107 }
108 
cpuinfo_get_cluster(uint32_t index)109 const struct cpuinfo_cluster* cpuinfo_get_cluster(uint32_t index) {
110 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
111 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cluster");
112 	}
113 	if CPUINFO_UNLIKELY(index >= cpuinfo_clusters_count) {
114 		return NULL;
115 	}
116 	return &cpuinfo_clusters[index];
117 }
118 
cpuinfo_get_package(uint32_t index)119 const struct cpuinfo_package* cpuinfo_get_package(uint32_t index) {
120 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
121 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "package");
122 	}
123 	if CPUINFO_UNLIKELY(index >= cpuinfo_packages_count) {
124 		return NULL;
125 	}
126 	return &cpuinfo_packages[index];
127 }
128 
cpuinfo_get_uarch(uint32_t index)129 const struct cpuinfo_uarch_info* cpuinfo_get_uarch(uint32_t index) {
130 	if (!cpuinfo_is_initialized) {
131 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarch");
132 	}
133 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
134 		if CPUINFO_UNLIKELY(index >= cpuinfo_uarchs_count) {
135 			return NULL;
136 		}
137 		return &cpuinfo_uarchs[index];
138 	#else
139 		if CPUINFO_UNLIKELY(index != 0) {
140 			return NULL;
141 		}
142 		return &cpuinfo_global_uarch;
143 	#endif
144 }
145 
cpuinfo_get_processors_count(void)146 uint32_t cpuinfo_get_processors_count(void) {
147 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
148 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "processors_count");
149 	}
150 	return cpuinfo_processors_count;
151 }
152 
cpuinfo_get_cores_count(void)153 uint32_t cpuinfo_get_cores_count(void) {
154 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
155 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "cores_count");
156 	}
157 	return cpuinfo_cores_count;
158 }
159 
cpuinfo_get_clusters_count(void)160 uint32_t cpuinfo_get_clusters_count(void) {
161 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
162 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "clusters_count");
163 	}
164 	return cpuinfo_clusters_count;
165 }
166 
cpuinfo_get_packages_count(void)167 uint32_t cpuinfo_get_packages_count(void) {
168 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
169 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "packages_count");
170 	}
171 	return cpuinfo_packages_count;
172 }
173 
cpuinfo_get_uarchs_count(void)174 uint32_t cpuinfo_get_uarchs_count(void) {
175 	if (!cpuinfo_is_initialized) {
176 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "uarchs_count");
177 	}
178 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
179 		return cpuinfo_uarchs_count;
180 	#else
181 		return 1;
182 	#endif
183 }
184 
cpuinfo_get_l1i_caches(void)185 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_caches(void) {
186 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
187 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches");
188 	}
189 	return cpuinfo_cache[cpuinfo_cache_level_1i];
190 }
191 
cpuinfo_get_l1d_caches(void)192 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_caches(void) {
193 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
194 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches");
195 	}
196 	return cpuinfo_cache[cpuinfo_cache_level_1d];
197 }
198 
cpuinfo_get_l2_caches(void)199 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_caches(void) {
200 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
201 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches");
202 	}
203 	return cpuinfo_cache[cpuinfo_cache_level_2];
204 }
205 
cpuinfo_get_l3_caches(void)206 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_caches(void) {
207 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
208 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches");
209 	}
210 	return cpuinfo_cache[cpuinfo_cache_level_3];
211 }
212 
cpuinfo_get_l4_caches(void)213 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_caches(void) {
214 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
215 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches");
216 	}
217 	return cpuinfo_cache[cpuinfo_cache_level_4];
218 }
219 
cpuinfo_get_l1i_cache(uint32_t index)220 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1i_cache(uint32_t index) {
221 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
222 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_cache");
223 	}
224 	if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_1i]) {
225 		return NULL;
226 	}
227 	return &cpuinfo_cache[cpuinfo_cache_level_1i][index];
228 }
229 
cpuinfo_get_l1d_cache(uint32_t index)230 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l1d_cache(uint32_t index) {
231 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
232 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_cache");
233 	}
234 	if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_1d]) {
235 		return NULL;
236 	}
237 	return &cpuinfo_cache[cpuinfo_cache_level_1d][index];
238 }
239 
cpuinfo_get_l2_cache(uint32_t index)240 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l2_cache(uint32_t index) {
241 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
242 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_cache");
243 	}
244 	if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_2]) {
245 		return NULL;
246 	}
247 	return &cpuinfo_cache[cpuinfo_cache_level_2][index];
248 }
249 
cpuinfo_get_l3_cache(uint32_t index)250 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l3_cache(uint32_t index) {
251 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
252 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_cache");
253 	}
254 	if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_3]) {
255 		return NULL;
256 	}
257 	return &cpuinfo_cache[cpuinfo_cache_level_3][index];
258 }
259 
cpuinfo_get_l4_cache(uint32_t index)260 const struct cpuinfo_cache* CPUINFO_ABI cpuinfo_get_l4_cache(uint32_t index) {
261 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
262 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_cache");
263 	}
264 	if CPUINFO_UNLIKELY(index >= cpuinfo_cache_count[cpuinfo_cache_level_4]) {
265 		return NULL;
266 	}
267 	return &cpuinfo_cache[cpuinfo_cache_level_4][index];
268 }
269 
cpuinfo_get_l1i_caches_count(void)270 uint32_t CPUINFO_ABI cpuinfo_get_l1i_caches_count(void) {
271 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
272 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1i_caches_count");
273 	}
274 	return cpuinfo_cache_count[cpuinfo_cache_level_1i];
275 }
276 
cpuinfo_get_l1d_caches_count(void)277 uint32_t CPUINFO_ABI cpuinfo_get_l1d_caches_count(void) {
278 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
279 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l1d_caches_count");
280 	}
281 	return cpuinfo_cache_count[cpuinfo_cache_level_1d];
282 }
283 
cpuinfo_get_l2_caches_count(void)284 uint32_t CPUINFO_ABI cpuinfo_get_l2_caches_count(void) {
285 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
286 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l2_caches_count");
287 	}
288 	return cpuinfo_cache_count[cpuinfo_cache_level_2];
289 }
290 
cpuinfo_get_l3_caches_count(void)291 uint32_t CPUINFO_ABI cpuinfo_get_l3_caches_count(void) {
292 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
293 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l3_caches_count");
294 	}
295 	return cpuinfo_cache_count[cpuinfo_cache_level_3];
296 }
297 
cpuinfo_get_l4_caches_count(void)298 uint32_t CPUINFO_ABI cpuinfo_get_l4_caches_count(void) {
299 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
300 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "l4_caches_count");
301 	}
302 	return cpuinfo_cache_count[cpuinfo_cache_level_4];
303 }
304 
cpuinfo_get_max_cache_size(void)305 uint32_t CPUINFO_ABI cpuinfo_get_max_cache_size(void) {
306 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
307 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "max_cache_size");
308 	}
309 	return cpuinfo_max_cache_size;
310 }
311 
cpuinfo_get_current_processor(void)312 const struct cpuinfo_processor* CPUINFO_ABI cpuinfo_get_current_processor(void) {
313 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
314 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_processor");
315 	}
316 	#ifdef __linux__
317 		/* Initializing this variable silences a MemorySanitizer error. */
318 		unsigned cpu = 0;
319 		if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
320 			return 0;
321 		}
322 		if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
323 			return 0;
324 		}
325 		return cpuinfo_linux_cpu_to_processor_map[cpu];
326 	#else
327 		return NULL;
328 	#endif
329 }
330 
cpuinfo_get_current_core(void)331 const struct cpuinfo_core* CPUINFO_ABI cpuinfo_get_current_core(void) {
332 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
333 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_core");
334 	}
335 	#ifdef __linux__
336 		/* Initializing this variable silences a MemorySanitizer error. */
337 		unsigned cpu = 0;
338 		if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
339 			return 0;
340 		}
341 		if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
342 			return 0;
343 		}
344 		return cpuinfo_linux_cpu_to_core_map[cpu];
345 	#else
346 		return NULL;
347 	#endif
348 }
349 
cpuinfo_get_current_uarch_index(void)350 uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index(void) {
351 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
352 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index");
353 	}
354 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
355 		#ifdef __linux__
356 			if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
357 				/* Special case: avoid syscall on systems with only a single type of cores */
358 				return 0;
359 			}
360 
361 			/* General case */
362 			/* Initializing this variable silences a MemorySanitizer error. */
363 			unsigned cpu = 0;
364 			if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
365 				return 0;
366 			}
367 			if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
368 				return 0;
369 			}
370 			return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
371 		#else
372 			/* Fallback: pretend to be on the big core. */
373 			return 0;
374 		#endif
375 	#else
376 		/* Only ARM/ARM64 processors may include cores of different types in the same package. */
377 		return 0;
378 	#endif
379 }
380 
cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index)381 uint32_t CPUINFO_ABI cpuinfo_get_current_uarch_index_with_default(uint32_t default_uarch_index) {
382 	if CPUINFO_UNLIKELY(!cpuinfo_is_initialized) {
383 		cpuinfo_log_fatal("cpuinfo_get_%s called before cpuinfo is initialized", "current_uarch_index_with_default");
384 	}
385 	#if CPUINFO_ARCH_ARM || CPUINFO_ARCH_ARM64
386 		#ifdef __linux__
387 			if (cpuinfo_linux_cpu_to_uarch_index_map == NULL) {
388 				/* Special case: avoid syscall on systems with only a single type of cores */
389 				return 0;
390 			}
391 
392 			/* General case */
393 			/* Initializing this variable silences a MemorySanitizer error. */
394 			unsigned cpu = 0;
395 			if CPUINFO_UNLIKELY(syscall(__NR_getcpu, &cpu, NULL, NULL) != 0) {
396 				return default_uarch_index;
397 			}
398 			if CPUINFO_UNLIKELY((uint32_t) cpu >= cpuinfo_linux_cpu_max) {
399 				return default_uarch_index;
400 			}
401 			return cpuinfo_linux_cpu_to_uarch_index_map[cpu];
402 		#else
403 			/* Fallback: no API to query current core, use default uarch index. */
404 			return default_uarch_index;
405 		#endif
406 	#else
407 		/* Only ARM/ARM64 processors may include cores of different types in the same package. */
408 		return 0;
409 	#endif
410 }
411