• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2012 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 #include <sys/system_properties.h>
12 #ifdef __arm__
13 #include <machine/cpu-features.h>
14 #endif
15 #include <pthread.h>
16 #include "cpu-features.h"
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 
24 static  pthread_once_t     g_once;
25 static  AndroidCpuFamily   g_cpuFamily;
26 static  uint64_t           g_cpuFeatures;
27 static  int                g_cpuCount;
28 
29 static const int  android_cpufeatures_debug = 0;
30 
31 #ifdef __arm__
32 #  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_ARM
33 #elif defined __i386__
34 #  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_X86
35 #else
36 #  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_UNKNOWN
37 #endif
38 
39 #define  D(...) \
40     do { \
41         if (android_cpufeatures_debug) { \
42             printf(__VA_ARGS__); fflush(stdout); \
43         } \
44     } while (0)
45 
46 #ifdef __i386__
x86_cpuid(int func,int values[4])47 static __inline__ void x86_cpuid(int func, int values[4])
48 {
49     int a, b, c, d;
50     /* We need to preserve ebx since we're compiling PIC code */
51     /* this means we can't use "=b" for the second output register */
52     __asm__ __volatile__ ( \
53       "push %%ebx\n"
54       "cpuid\n" \
55       "mov %1, %%ebx\n"
56       "pop %%ebx\n"
57       : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
58       : "a" (func) \
59     );
60     values[0] = a;
61     values[1] = b;
62     values[2] = c;
63     values[3] = d;
64 }
65 #endif
66 
67 /* Read the content of /proc/cpuinfo into a user-provided buffer.
68  * Return the length of the data, or -1 on error. Does *not*
69  * zero-terminate the content. Will not read more
70  * than 'buffsize' bytes.
71  */
72 static int
read_file(const char * pathname,char * buffer,size_t buffsize)73 read_file(const char*  pathname, char*  buffer, size_t  buffsize)
74 {
75     int  fd, len;
76 
77     fd = open(pathname, O_RDONLY);
78     if (fd < 0)
79         return -1;
80 
81     do {
82         len = read(fd, buffer, buffsize);
83     } while (len < 0 && errno == EINTR);
84 
85     close(fd);
86 
87     return len;
88 }
89 
90 /* Extract the content of a the first occurence of a given field in
91  * the content of /proc/cpuinfo and return it as a heap-allocated
92  * string that must be freed by the caller.
93  *
94  * Return NULL if not found
95  */
96 static char*
extract_cpuinfo_field(char * buffer,int buflen,const char * field)97 extract_cpuinfo_field(char* buffer, int buflen, const char* field)
98 {
99     int  fieldlen = strlen(field);
100     char* bufend = buffer + buflen;
101     char* result = NULL;
102     int len, ignore;
103     const char *p, *q;
104 
105     /* Look for first field occurence, and ensures it starts the line.
106      */
107     p = buffer;
108     bufend = buffer + buflen;
109     for (;;) {
110         p = memmem(p, bufend-p, field, fieldlen);
111         if (p == NULL)
112             goto EXIT;
113 
114         if (p == buffer || p[-1] == '\n')
115             break;
116 
117         p += fieldlen;
118     }
119 
120     /* Skip to the first column followed by a space */
121     p += fieldlen;
122     p  = memchr(p, ':', bufend-p);
123     if (p == NULL || p[1] != ' ')
124         goto EXIT;
125 
126     /* Find the end of the line */
127     p += 2;
128     q = memchr(p, '\n', bufend-p);
129     if (q == NULL)
130         q = bufend;
131 
132     /* Copy the line into a heap-allocated buffer */
133     len = q-p;
134     result = malloc(len+1);
135     if (result == NULL)
136         goto EXIT;
137 
138     memcpy(result, p, len);
139     result[len] = '\0';
140 
141 EXIT:
142     return result;
143 }
144 
145 /* Count the number of occurences of a given field prefix in /proc/cpuinfo.
146  */
147 static int
count_cpuinfo_field(char * buffer,int buflen,const char * field)148 count_cpuinfo_field(char* buffer, int buflen, const char* field)
149 {
150     int fieldlen = strlen(field);
151     const char* p = buffer;
152     const char* bufend = buffer + buflen;
153     const char* q;
154     int count = 0;
155 
156     for (;;) {
157         const char* q;
158 
159         p = memmem(p, bufend-p, field, fieldlen);
160         if (p == NULL)
161             break;
162 
163         /* Ensure that the field is at the start of a line */
164         if (p > buffer && p[-1] != '\n') {
165             p += fieldlen;
166             continue;
167         }
168 
169 
170         /* skip any whitespace */
171         q = p + fieldlen;
172         while (q < bufend && (*q == ' ' || *q == '\t'))
173             q++;
174 
175         /* we must have a colon now */
176         if (q < bufend && *q == ':') {
177             count += 1;
178             q ++;
179         }
180         p = q;
181     }
182 
183     return count;
184 }
185 
186 /* Like strlen(), but for constant string literals */
187 #define STRLEN_CONST(x)  ((sizeof(x)-1)
188 
189 
190 /* Checks that a space-separated list of items contains one given 'item'.
191  * Returns 1 if found, 0 otherwise.
192  */
193 static int
has_list_item(const char * list,const char * item)194 has_list_item(const char* list, const char* item)
195 {
196     const char*  p = list;
197     int itemlen = strlen(item);
198 
199     if (list == NULL)
200         return 0;
201 
202     while (*p) {
203         const char*  q;
204 
205         /* skip spaces */
206         while (*p == ' ' || *p == '\t')
207             p++;
208 
209         /* find end of current list item */
210         q = p;
211         while (*q && *q != ' ' && *q != '\t')
212             q++;
213 
214         if (itemlen == q-p && !memcmp(p, item, itemlen))
215             return 1;
216 
217         /* skip to next item */
218         p = q;
219     }
220     return 0;
221 }
222 
223 
224 static void
android_cpuInit(void)225 android_cpuInit(void)
226 {
227     char cpuinfo[4096];
228     int  cpuinfo_len;
229 
230     g_cpuFamily   = DEFAULT_CPU_FAMILY;
231     g_cpuFeatures = 0;
232     g_cpuCount    = 1;
233 
234     cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
235     D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
236       cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
237 
238     if (cpuinfo_len < 0)  /* should not happen */ {
239         return;
240     }
241 
242     /* Count the CPU cores, the value may be 0 for single-core CPUs */
243     g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
244     if (g_cpuCount == 0) {
245         g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
246         if (g_cpuCount == 0) {
247             g_cpuCount = 1;
248         }
249     }
250 
251     D("found cpuCount = %d\n", g_cpuCount);
252 
253 #ifdef __ARM_ARCH__
254     {
255         char*  features = NULL;
256         char*  architecture = NULL;
257 
258         /* Extract architecture from the "CPU Architecture" field.
259          * The list is well-known, unlike the the output of
260          * the 'Processor' field which can vary greatly.
261          *
262          * See the definition of the 'proc_arch' array in
263          * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
264          * same file.
265          */
266         char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
267 
268         if (cpuArch != NULL) {
269             char*  end;
270             long   archNumber;
271             int    hasARMv7 = 0;
272 
273             D("found cpuArch = '%s'\n", cpuArch);
274 
275             /* read the initial decimal number, ignore the rest */
276             archNumber = strtol(cpuArch, &end, 10);
277 
278             /* Here we assume that ARMv8 will be upwards compatible with v7
279                 * in the future. Unfortunately, there is no 'Features' field to
280                 * indicate that Thumb-2 is supported.
281                 */
282             if (end > cpuArch && archNumber >= 7) {
283                 hasARMv7 = 1;
284             }
285 
286             /* Unfortunately, it seems that certain ARMv6-based CPUs
287              * report an incorrect architecture number of 7!
288              *
289              * See http://code.google.com/p/android/issues/detail?id=10812
290              *
291              * We try to correct this by looking at the 'elf_format'
292              * field reported by the 'Processor' field, which is of the
293              * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
294              * an ARMv6-one.
295              */
296             if (hasARMv7) {
297                 char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
298                                                       "Processor");
299                 if (cpuProc != NULL) {
300                     D("found cpuProc = '%s'\n", cpuProc);
301                     if (has_list_item(cpuProc, "(v6l)")) {
302                         D("CPU processor and architecture mismatch!!\n");
303                         hasARMv7 = 0;
304                     }
305                     free(cpuProc);
306                 }
307             }
308 
309             if (hasARMv7) {
310                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
311             }
312 
313             /* The LDREX / STREX instructions are available from ARMv6 */
314             if (archNumber >= 6) {
315                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX;
316             }
317 
318             free(cpuArch);
319         }
320 
321         /* Extract the list of CPU features from 'Features' field */
322         char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
323 
324         if (cpuFeatures != NULL) {
325 
326             D("found cpuFeatures = '%s'\n", cpuFeatures);
327 
328             if (has_list_item(cpuFeatures, "vfpv3"))
329                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
330 
331             else if (has_list_item(cpuFeatures, "vfpv3d16"))
332                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
333 
334             if (has_list_item(cpuFeatures, "neon")) {
335                 /* Note: Certain kernels only report neon but not vfpv3
336                     *       in their features list. However, ARM mandates
337                     *       that if Neon is implemented, so must be VFPv3
338                     *       so always set the flag.
339                     */
340                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON |
341                                  ANDROID_CPU_ARM_FEATURE_VFPv3;
342             }
343             free(cpuFeatures);
344         }
345     }
346 #endif /* __ARM_ARCH__ */
347 
348 #ifdef __i386__
349     g_cpuFamily = ANDROID_CPU_FAMILY_X86;
350 
351     int regs[4];
352 
353 /* According to http://en.wikipedia.org/wiki/CPUID */
354 #define VENDOR_INTEL_b  0x756e6547
355 #define VENDOR_INTEL_c  0x6c65746e
356 #define VENDOR_INTEL_d  0x49656e69
357 
358     x86_cpuid(0, regs);
359     int vendorIsIntel = (regs[1] == VENDOR_INTEL_b &&
360                          regs[2] == VENDOR_INTEL_c &&
361                          regs[3] == VENDOR_INTEL_d);
362 
363     x86_cpuid(1, regs);
364     if ((regs[2] & (1 << 9)) != 0) {
365         g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3;
366     }
367     if ((regs[2] & (1 << 23)) != 0) {
368         g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT;
369     }
370     if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) {
371         g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE;
372     }
373 #endif
374 }
375 
376 
377 AndroidCpuFamily
android_getCpuFamily(void)378 android_getCpuFamily(void)
379 {
380     pthread_once(&g_once, android_cpuInit);
381     return g_cpuFamily;
382 }
383 
384 
385 uint64_t
android_getCpuFeatures(void)386 android_getCpuFeatures(void)
387 {
388     pthread_once(&g_once, android_cpuInit);
389     return g_cpuFeatures;
390 }
391 
392 
393 int
android_getCpuCount(void)394 android_getCpuCount(void)
395 {
396     pthread_once(&g_once, android_cpuInit);
397     return g_cpuCount;
398 }
399