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