1 /*
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 // This file is derived from Android's NDK package r7, located at
12 // <ndk>/sources/android/cpufeatures/ (downloadable from
13 // http://developer.android.com/sdk/ndk/index.html).
14
15 #include "cpu_features_wrapper.h"
16
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <pthread.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 // Define CPU family.
24 typedef enum {
25 CPU_FAMILY_UNKNOWN = 0,
26 CPU_FAMILY_ARM,
27 CPU_FAMILY_X86,
28 CPU_FAMILY_MAX // Do not remove.
29 } CpuFamily;
30
31 static pthread_once_t g_once;
32 static CpuFamily g_cpuFamily;
33 static uint64_t g_cpuFeatures;
34 static int g_cpuCount;
35
36 static const int cpufeatures_debug = 0;
37
38 #ifdef __arm__
39 # define DEFAULT_CPU_FAMILY CPU_FAMILY_ARM
40 #elif defined __i386__
41 # define DEFAULT_CPU_FAMILY CPU_FAMILY_X86
42 #else
43 # define DEFAULT_CPU_FAMILY CPU_FAMILY_UNKNOWN
44 #endif
45
46 #define D(...) \
47 do { \
48 if (cpufeatures_debug) { \
49 printf(__VA_ARGS__); fflush(stdout); \
50 } \
51 } while (0)
52
53 /* Read the content of /proc/cpuinfo into a user-provided buffer.
54 * Return the length of the data, or -1 on error. Does *not*
55 * zero-terminate the content. Will not read more
56 * than 'buffsize' bytes.
57 */
read_file(const char * pathname,char * buffer,size_t buffsize)58 static int read_file(const char* pathname, char* buffer, size_t buffsize) {
59 int fd, len;
60
61 fd = open(pathname, O_RDONLY);
62 if (fd < 0)
63 return -1;
64
65 do {
66 len = read(fd, buffer, buffsize);
67 } while (len < 0 && errno == EINTR);
68
69 close(fd);
70
71 return len;
72 }
73
74 /* Extract the content of a the first occurence of a given field in
75 * the content of /proc/cpuinfo and return it as a heap-allocated
76 * string that must be freed by the caller.
77 *
78 * Return NULL if not found
79 */
extract_cpuinfo_field(char * buffer,int buflen,const char * field)80 static char* extract_cpuinfo_field(char* buffer, int buflen, const char* field) {
81 int fieldlen = strlen(field);
82 char* bufend = buffer + buflen;
83 char* result = NULL;
84 int len, ignore;
85 const char* p, *q;
86
87 /* Look for first field occurence, and ensures it starts the line.
88 */
89 p = buffer;
90 bufend = buffer + buflen;
91 for (;;) {
92 p = memmem(p, bufend - p, field, fieldlen);
93 if (p == NULL)
94 goto EXIT;
95
96 if (p == buffer || p[-1] == '\n')
97 break;
98
99 p += fieldlen;
100 }
101
102 /* Skip to the first column followed by a space */
103 p += fieldlen;
104 p = memchr(p, ':', bufend - p);
105 if (p == NULL || p[1] != ' ')
106 goto EXIT;
107
108 /* Find the end of the line */
109 p += 2;
110 q = memchr(p, '\n', bufend - p);
111 if (q == NULL)
112 q = bufend;
113
114 /* Copy the line into a heap-allocated buffer */
115 len = q - p;
116 result = malloc(len + 1);
117 if (result == NULL)
118 goto EXIT;
119
120 memcpy(result, p, len);
121 result[len] = '\0';
122
123 EXIT:
124 return result;
125 }
126
127 /* Count the number of occurences of a given field prefix in /proc/cpuinfo.
128 */
count_cpuinfo_field(char * buffer,int buflen,const char * field)129 static int count_cpuinfo_field(char* buffer, int buflen, const char* field) {
130 int fieldlen = strlen(field);
131 const char* p = buffer;
132 const char* bufend = buffer + buflen;
133 const char* q;
134 int count = 0;
135
136 for (;;) {
137 const char* q;
138
139 p = memmem(p, bufend - p, field, fieldlen);
140 if (p == NULL)
141 break;
142
143 /* Ensure that the field is at the start of a line */
144 if (p > buffer && p[-1] != '\n') {
145 p += fieldlen;
146 continue;
147 }
148
149
150 /* skip any whitespace */
151 q = p + fieldlen;
152 while (q < bufend && (*q == ' ' || *q == '\t'))
153 q++;
154
155 /* we must have a colon now */
156 if (q < bufend && *q == ':') {
157 count += 1;
158 q ++;
159 }
160 p = q;
161 }
162
163 return count;
164 }
165
166 /* Like strlen(), but for constant string literals */
167 #define STRLEN_CONST(x) ((sizeof(x)-1)
168
169
170 /* Checks that a space-separated list of items contains one given 'item'.
171 * Returns 1 if found, 0 otherwise.
172 */
has_list_item(const char * list,const char * item)173 static int has_list_item(const char* list, const char* item) {
174 const char* p = list;
175 int itemlen = strlen(item);
176
177 if (list == NULL)
178 return 0;
179
180 while (*p) {
181 const char* q;
182
183 /* skip spaces */
184 while (*p == ' ' || *p == '\t')
185 p++;
186
187 /* find end of current list item */
188 q = p;
189 while (*q && *q != ' ' && *q != '\t')
190 q++;
191
192 if (itemlen == q - p && !memcmp(p, item, itemlen))
193 return 1;
194
195 /* skip to next item */
196 p = q;
197 }
198 return 0;
199 }
200
201
cpuInit(void)202 static void cpuInit(void) {
203 char cpuinfo[4096];
204 int cpuinfo_len;
205
206 g_cpuFamily = DEFAULT_CPU_FAMILY;
207 g_cpuFeatures = 0;
208 g_cpuCount = 1;
209
210 cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
211 D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
212 cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
213
214 if (cpuinfo_len < 0) { /* should not happen */
215 return;
216 }
217
218 /* Count the CPU cores, the value may be 0 for single-core CPUs */
219 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
220 if (g_cpuCount == 0) {
221 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
222 if (g_cpuCount == 0) {
223 g_cpuCount = 1;
224 }
225 }
226
227 D("found cpuCount = %d\n", g_cpuCount);
228
229 #ifdef __arm__
230 {
231 char* features = NULL;
232 char* architecture = NULL;
233
234 /* Extract architecture from the "CPU Architecture" field.
235 * The list is well-known, unlike the the output of
236 * the 'Processor' field which can vary greatly.
237 *
238 * See the definition of the 'proc_arch' array in
239 * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
240 * same file.
241 */
242 char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
243 "CPU architecture");
244
245 if (cpuArch != NULL) {
246 char* end;
247 long archNumber;
248 int hasARMv7 = 0;
249
250 D("found cpuArch = '%s'\n", cpuArch);
251
252 /* read the initial decimal number, ignore the rest */
253 archNumber = strtol(cpuArch, &end, 10);
254
255 /* Here we assume that ARMv8 will be upwards compatible with v7
256 * in the future. Unfortunately, there is no 'Features' field to
257 * indicate that Thumb-2 is supported.
258 */
259 if (end > cpuArch && archNumber >= 7) {
260 hasARMv7 = 1;
261 }
262
263 /* Unfortunately, it seems that certain ARMv6-based CPUs
264 * report an incorrect architecture number of 7!
265 *
266 * We try to correct this by looking at the 'elf_format'
267 * field reported by the 'Processor' field, which is of the
268 * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
269 * an ARMv6-one.
270 */
271 if (hasARMv7) {
272 char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
273 "Processor");
274 if (cpuProc != NULL) {
275 D("found cpuProc = '%s'\n", cpuProc);
276 if (has_list_item(cpuProc, "(v6l)")) {
277 D("CPU processor and architecture mismatch!!\n");
278 hasARMv7 = 0;
279 }
280 free(cpuProc);
281 }
282 }
283
284 if (hasARMv7) {
285 g_cpuFeatures |= kCPUFeatureARMv7;
286 }
287
288 /* The LDREX / STREX instructions are available from ARMv6 */
289 if (archNumber >= 6) {
290 g_cpuFeatures |= kCPUFeatureLDREXSTREX;
291 }
292
293 free(cpuArch);
294 }
295
296 /* Extract the list of CPU features from 'Features' field */
297 char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
298 "Features");
299
300 if (cpuFeatures != NULL) {
301
302 D("found cpuFeatures = '%s'\n", cpuFeatures);
303
304 if (has_list_item(cpuFeatures, "vfpv3"))
305 g_cpuFeatures |= kCPUFeatureVFPv3;
306
307 else if (has_list_item(cpuFeatures, "vfpv3d16"))
308 g_cpuFeatures |= kCPUFeatureVFPv3;
309
310 if (has_list_item(cpuFeatures, "neon")) {
311 /* Note: Certain kernels only report neon but not vfpv3
312 * in their features list. However, ARM mandates
313 * that if Neon is implemented, so must be VFPv3
314 * so always set the flag.
315 */
316 g_cpuFeatures |= kCPUFeatureNEON |
317 kCPUFeatureVFPv3;
318 }
319 free(cpuFeatures);
320 }
321 }
322 #endif // __arm__
323
324 #ifdef __i386__
325 g_cpuFamily = CPU_FAMILY_X86;
326 #endif
327 }
328
329
WebRtc_GetCPUFeaturesARM(void)330 uint64_t WebRtc_GetCPUFeaturesARM(void) {
331 pthread_once(&g_once, cpuInit);
332 return g_cpuFeatures;
333 }
334