Home | History | Annotate | Download | only in dsp
      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__
     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 %%ebx, %1\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
     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*
     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
    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
    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
    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
    378 android_getCpuFamily(void)
    379 {
    380     pthread_once(&g_once, android_cpuInit);
    381     return g_cpuFamily;
    382 }
    383 
    384 
    385 uint64_t
    386 android_getCpuFeatures(void)
    387 {
    388     pthread_once(&g_once, android_cpuInit);
    389     return g_cpuFeatures;
    390 }
    391 
    392 
    393 int
    394 android_getCpuCount(void)
    395 {
    396     pthread_once(&g_once, android_cpuInit);
    397     return g_cpuCount;
    398 }
    399