Home | History | Annotate | Download | only in cpufeatures
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 /* ChangeLog for this library:
     30  *
     31  * NDK r5: Handle buggy kernels which report a CPU Architecture number of 7
     32  *         for an ARMv6 CPU (see below).
     33  *
     34  *         Handle kernels that only report 'neon', and not 'vfpv3'
     35  *         (VFPv3 is mandated by the ARM architecture is Neon is implemented)
     36  *
     37  *         Handle kernels that only report 'vfpv3d16', and not 'vfpv3'
     38  *
     39  *         Fix x86 compilation. Report ANDROID_CPU_FAMILY_X86 in
     40  *         android_getCpuFamily().
     41  *
     42  * NDK r4: Initial release
     43  */
     44 #include <sys/system_properties.h>
     45 #ifdef __arm__
     46 #include <machine/cpu-features.h>
     47 #endif
     48 #include <pthread.h>
     49 #include "cpu-features.h"
     50 #include <stdio.h>
     51 #include <stdlib.h>
     52 #include <fcntl.h>
     53 #include <errno.h>
     54 
     55 static  pthread_once_t     g_once;
     56 static  AndroidCpuFamily   g_cpuFamily;
     57 static  uint64_t           g_cpuFeatures;
     58 static  int                g_cpuCount;
     59 
     60 static const int  android_cpufeatures_debug = 0;
     61 
     62 #ifdef __arm__
     63 #  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_ARM
     64 #elif defined __i386__
     65 #  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_X86
     66 #else
     67 #  define DEFAULT_CPU_FAMILY  ANDROID_CPU_FAMILY_UNKNOWN
     68 #endif
     69 
     70 #define  D(...) \
     71     do { \
     72         if (android_cpufeatures_debug) { \
     73             printf(__VA_ARGS__); fflush(stdout); \
     74         } \
     75     } while (0)
     76 
     77 /* Read the content of /proc/cpuinfo into a user-provided buffer.
     78  * Return the length of the data, or -1 on error. Does *not*
     79  * zero-terminate the content. Will not read more
     80  * than 'buffsize' bytes.
     81  */
     82 static int
     83 read_file(const char*  pathname, char*  buffer, size_t  buffsize)
     84 {
     85     int  fd, len;
     86 
     87     fd = open(pathname, O_RDONLY);
     88     if (fd < 0)
     89         return -1;
     90 
     91     do {
     92         len = read(fd, buffer, buffsize);
     93     } while (len < 0 && errno == EINTR);
     94 
     95     close(fd);
     96 
     97     return len;
     98 }
     99 
    100 /* Extract the content of a the first occurence of a given field in
    101  * the content of /proc/cpuinfo and return it as a heap-allocated
    102  * string that must be freed by the caller.
    103  *
    104  * Return NULL if not found
    105  */
    106 static char*
    107 extract_cpuinfo_field(char* buffer, int buflen, const char* field)
    108 {
    109     int  fieldlen = strlen(field);
    110     char* bufend = buffer + buflen;
    111     char* result = NULL;
    112     int len, ignore;
    113     const char *p, *q;
    114 
    115     /* Look for first field occurence, and ensures it starts the line.
    116      */
    117     p = buffer;
    118     bufend = buffer + buflen;
    119     for (;;) {
    120         p = memmem(p, bufend-p, field, fieldlen);
    121         if (p == NULL)
    122             goto EXIT;
    123 
    124         if (p == buffer || p[-1] == '\n')
    125             break;
    126 
    127         p += fieldlen;
    128     }
    129 
    130     /* Skip to the first column followed by a space */
    131     p += fieldlen;
    132     p  = memchr(p, ':', bufend-p);
    133     if (p == NULL || p[1] != ' ')
    134         goto EXIT;
    135 
    136     /* Find the end of the line */
    137     p += 2;
    138     q = memchr(p, '\n', bufend-p);
    139     if (q == NULL)
    140         q = bufend;
    141 
    142     /* Copy the line into a heap-allocated buffer */
    143     len = q-p;
    144     result = malloc(len+1);
    145     if (result == NULL)
    146         goto EXIT;
    147 
    148     memcpy(result, p, len);
    149     result[len] = '\0';
    150 
    151 EXIT:
    152     return result;
    153 }
    154 
    155 /* Count the number of occurences of a given field prefix in /proc/cpuinfo.
    156  */
    157 static int
    158 count_cpuinfo_field(char* buffer, int buflen, const char* field)
    159 {
    160     int fieldlen = strlen(field);
    161     const char* p = buffer;
    162     const char* bufend = buffer + buflen;
    163     const char* q;
    164     int count = 0;
    165 
    166     for (;;) {
    167         const char* q;
    168 
    169         p = memmem(p, bufend-p, field, fieldlen);
    170         if (p == NULL)
    171             break;
    172 
    173         /* Ensure that the field is at the start of a line */
    174         if (p > buffer && p[-1] != '\n') {
    175             p += fieldlen;
    176             continue;
    177         }
    178 
    179 
    180         /* skip any whitespace */
    181         q = p + fieldlen;
    182         while (q < bufend && (*q == ' ' || *q == '\t'))
    183             q++;
    184 
    185         /* we must have a colon now */
    186         if (q < bufend && *q == ':') {
    187             count += 1;
    188             q ++;
    189         }
    190         p = q;
    191     }
    192 
    193     return count;
    194 }
    195 
    196 /* Like strlen(), but for constant string literals */
    197 #define STRLEN_CONST(x)  ((sizeof(x)-1)
    198 
    199 
    200 /* Checks that a space-separated list of items contains one given 'item'.
    201  * Returns 1 if found, 0 otherwise.
    202  */
    203 static int
    204 has_list_item(const char* list, const char* item)
    205 {
    206     const char*  p = list;
    207     int itemlen = strlen(item);
    208 
    209     if (list == NULL)
    210         return 0;
    211 
    212     while (*p) {
    213         const char*  q;
    214 
    215         /* skip spaces */
    216         while (*p == ' ' || *p == '\t')
    217             p++;
    218 
    219         /* find end of current list item */
    220         q = p;
    221         while (*q && *q != ' ' && *q != '\t')
    222             q++;
    223 
    224         if (itemlen == q-p && !memcmp(p, item, itemlen))
    225             return 1;
    226 
    227         /* skip to next item */
    228         p = q;
    229     }
    230     return 0;
    231 }
    232 
    233 
    234 static void
    235 android_cpuInit(void)
    236 {
    237     char cpuinfo[4096];
    238     int  cpuinfo_len;
    239 
    240     g_cpuFamily   = DEFAULT_CPU_FAMILY;
    241     g_cpuFeatures = 0;
    242     g_cpuCount    = 1;
    243 
    244     cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
    245     D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
    246       cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
    247 
    248     if (cpuinfo_len < 0)  /* should not happen */ {
    249         return;
    250     }
    251 
    252     /* Count the CPU cores, the value may be 0 for single-core CPUs */
    253     g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
    254     if (g_cpuCount == 0) {
    255         g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
    256         if (g_cpuCount == 0) {
    257             g_cpuCount = 1;
    258         }
    259     }
    260 
    261     D("found cpuCount = %d\n", g_cpuCount);
    262 
    263 #ifdef __ARM_ARCH__
    264     {
    265         char*  features = NULL;
    266         char*  architecture = NULL;
    267 
    268         /* Extract architecture from the "CPU Architecture" field.
    269          * The list is well-known, unlike the the output of
    270          * the 'Processor' field which can vary greatly.
    271          *
    272          * See the definition of the 'proc_arch' array in
    273          * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
    274          * same file.
    275          */
    276         char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
    277 
    278         if (cpuArch != NULL) {
    279             char*  end;
    280             long   archNumber;
    281             int    hasARMv7 = 0;
    282 
    283             D("found cpuArch = '%s'\n", cpuArch);
    284 
    285             /* read the initial decimal number, ignore the rest */
    286             archNumber = strtol(cpuArch, &end, 10);
    287 
    288             /* Here we assume that ARMv8 will be upwards compatible with v7
    289                 * in the future. Unfortunately, there is no 'Features' field to
    290                 * indicate that Thumb-2 is supported.
    291                 */
    292             if (end > cpuArch && archNumber >= 7) {
    293                 hasARMv7 = 1;
    294             }
    295 
    296             /* Unfortunately, it seems that certain ARMv6-based CPUs
    297              * report an incorrect architecture number of 7!
    298              *
    299              * See http://code.google.com/p/android/issues/detail?id=10812
    300              *
    301              * We try to correct this by looking at the 'elf_format'
    302              * field reported by the 'Processor' field, which is of the
    303              * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
    304              * an ARMv6-one.
    305              */
    306             if (hasARMv7) {
    307                 char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
    308                                                       "Processor");
    309                 if (cpuProc != NULL) {
    310                     D("found cpuProc = '%s'\n", cpuProc);
    311                     if (has_list_item(cpuProc, "(v6l)")) {
    312                         D("CPU processor and architecture mismatch!!\n");
    313                         hasARMv7 = 0;
    314                     }
    315                     free(cpuProc);
    316                 }
    317             }
    318 
    319             if (hasARMv7) {
    320                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
    321             }
    322 
    323             /* The LDREX / STREX instructions are available from ARMv6 */
    324             if (archNumber >= 6) {
    325                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX;
    326             }
    327 
    328             free(cpuArch);
    329         }
    330 
    331         /* Extract the list of CPU features from 'Features' field */
    332         char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
    333 
    334         if (cpuFeatures != NULL) {
    335 
    336             D("found cpuFeatures = '%s'\n", cpuFeatures);
    337 
    338             if (has_list_item(cpuFeatures, "vfpv3"))
    339                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
    340 
    341             else if (has_list_item(cpuFeatures, "vfpv3d16"))
    342                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
    343 
    344             if (has_list_item(cpuFeatures, "neon")) {
    345                 /* Note: Certain kernels only report neon but not vfpv3
    346                     *       in their features list. However, ARM mandates
    347                     *       that if Neon is implemented, so must be VFPv3
    348                     *       so always set the flag.
    349                     */
    350                 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON |
    351                                  ANDROID_CPU_ARM_FEATURE_VFPv3;
    352             }
    353             free(cpuFeatures);
    354         }
    355     }
    356 #endif /* __ARM_ARCH__ */
    357 
    358 #ifdef __i386__
    359     g_cpuFamily = ANDROID_CPU_FAMILY_X86;
    360 #endif
    361 }
    362 
    363 
    364 AndroidCpuFamily
    365 android_getCpuFamily(void)
    366 {
    367     pthread_once(&g_once, android_cpuInit);
    368     return g_cpuFamily;
    369 }
    370 
    371 
    372 uint64_t
    373 android_getCpuFeatures(void)
    374 {
    375     pthread_once(&g_once, android_cpuInit);
    376     return g_cpuFeatures;
    377 }
    378 
    379 
    380 int
    381 android_getCpuCount(void)
    382 {
    383     pthread_once(&g_once, android_cpuInit);
    384     return g_cpuCount;
    385 }
    386