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_ARCH__
     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 
     59 static const int  android_cpufeatures_debug = 0;
     60 
     61 #define  D(...) \
     62     do { \
     63         if (android_cpufeatures_debug) { \
     64             printf(__VA_ARGS__); fflush(stdout); \
     65         } \
     66     } while (0)
     67 
     68 /* Read the content of /proc/cpuinfo into a user-provided buffer.
     69  * Return the length of the data, or -1 on error. Does *not*
     70  * zero-terminate the content. Will not read more
     71  * than 'buffsize' bytes.
     72  */
     73 static int
     74 read_file(const char*  pathname, char*  buffer, size_t  buffsize)
     75 {
     76     int  fd, len;
     77 
     78     fd = open(pathname, O_RDONLY);
     79     if (fd < 0)
     80         return -1;
     81 
     82     do {
     83         len = read(fd, buffer, buffsize);
     84     } while (len < 0 && errno == EINTR);
     85 
     86     close(fd);
     87 
     88     return len;
     89 }
     90 
     91 /* Extract the content of a the first occurence of a given field in
     92  * the content of /proc/cpuinfo and return it as a heap-allocated
     93  * string that must be freed by the caller.
     94  *
     95  * Return NULL if not found
     96  */
     97 static char*
     98 extract_cpuinfo_field(char* buffer, int buflen, const char* field)
     99 {
    100     int  fieldlen = strlen(field);
    101     char* bufend = buffer + buflen;
    102     char* result = NULL;
    103     int len, ignore;
    104     const char *p, *q;
    105 
    106     /* Look for first field occurence, and ensures it starts the line.
    107      */
    108     p = buffer;
    109     bufend = buffer + buflen;
    110     for (;;) {
    111         p = memmem(p, bufend-p, field, fieldlen);
    112         if (p == NULL)
    113             goto EXIT;
    114 
    115         if (p == buffer || p[-1] == '\n')
    116             break;
    117 
    118         p += fieldlen;
    119     }
    120 
    121     /* Skip to the first column followed by a space */
    122     p += fieldlen;
    123     p  = memchr(p, ':', bufend-p);
    124     if (p == NULL || p[1] != ' ')
    125         goto EXIT;
    126 
    127     /* Find the end of the line */
    128     p += 2;
    129     q = memchr(p, '\n', bufend-p);
    130     if (q == NULL)
    131         q = bufend;
    132 
    133     /* Copy the line into a heap-allocated buffer */
    134     len = q-p;
    135     result = malloc(len+1);
    136     if (result == NULL)
    137         goto EXIT;
    138 
    139     memcpy(result, p, len);
    140     result[len] = '\0';
    141 
    142 EXIT:
    143     return result;
    144 }
    145 
    146 
    147 /* Checks that a space-separated list of items contains one given 'item'.
    148  * Returns 1 if found, 0 otherwise.
    149  */
    150 static int
    151 has_list_item(const char* list, const char* item)
    152 {
    153     const char*  p = list;
    154     int itemlen = strlen(item);
    155 
    156     if (list == NULL)
    157         return 0;
    158 
    159     while (*p) {
    160         const char*  q;
    161 
    162         /* skip spaces */
    163         while (*p == ' ' || *p == '\t')
    164             p++;
    165 
    166         /* find end of current list item */
    167         q = p;
    168         while (*q && *q != ' ' && *q != '\t')
    169             q++;
    170 
    171         if (itemlen == q-p && !memcmp(p, item, itemlen))
    172             return 1;
    173 
    174         /* skip to next item */
    175         p = q;
    176     }
    177     return 0;
    178 }
    179 
    180 
    181 static void
    182 android_cpuInit(void)
    183 {
    184     g_cpuFamily   = ANDROID_CPU_FAMILY_UNKNOWN;
    185     g_cpuFeatures = 0;
    186 
    187 #ifdef __ARM_ARCH__
    188     {
    189         char   cpuinfo[4096];
    190         int    cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
    191         char*  features = NULL;
    192         char*  architecture = NULL;
    193 
    194         g_cpuFamily = ANDROID_CPU_FAMILY_ARM;
    195 
    196         D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, cpuinfo_len, cpuinfo);
    197 
    198         if (cpuinfo_len < 0)  /* should not happen */ {
    199             return;
    200         }
    201 
    202         /* Extract architecture from the "CPU Architecture" field.
    203          * The list is well-known, unlike the the output of
    204          * the 'Processor' field which can vary greatly.
    205          *
    206          * See the definition of the 'proc_arch' array in
    207          * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
    208          * same file.
    209          */
    210         {
    211             char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
    212 
    213             if (cpuArch != NULL) {
    214                 char*  end;
    215                 long   archNumber;
    216                 int    hasARMv7 = 0;
    217 
    218                 D("found cpuArch = '%s'\n", cpuArch);
    219 
    220                 /* read the initial decimal number, ignore the rest */
    221                 archNumber = strtol(cpuArch, &end, 10);
    222 
    223                 /* Here we assume that ARMv8 will be upwards compatible with v7
    224                  * in the future. Unfortunately, there is no 'Features' field to
    225                  * indicate that Thumb-2 is supported.
    226                  */
    227                 if (end > cpuArch && archNumber >= 7) {
    228                     hasARMv7 = 1;
    229                 }
    230 
    231                 /* Unfortunately, it seems that certain ARMv6-based CPUs
    232                  * report an incorrect architecture number of 7!
    233                  *
    234                  * See http://code.google.com/p/android/issues/detail?id=10812
    235                  *
    236                  * We try to correct this by looking at the 'elf_format'
    237                  * field reported by the 'Processor' field, which is of the
    238                  * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
    239                  * an ARMv6-one.
    240                  */
    241                 if (hasARMv7) {
    242                     char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
    243                                                           "Processor");
    244                     if (cpuProc != NULL) {
    245                         D("found cpuProc = '%s'\n", cpuProc);
    246                         if (has_list_item(cpuProc, "(v6l)")) {
    247                             D("CPU processor and architecture mismatch!!\n");
    248                             hasARMv7 = 0;
    249                         }
    250                         free(cpuProc);
    251                     }
    252                 }
    253 
    254                 if (hasARMv7) {
    255                     g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
    256                 }
    257 
    258                 free(cpuArch);
    259             }
    260         }
    261 
    262         /* Extract the list of CPU features from 'Features' field */
    263         {
    264             char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
    265 
    266             if (cpuFeatures != NULL) {
    267 
    268                 D("found cpuFeatures = '%s'\n", cpuFeatures);
    269 
    270                 if (has_list_item(cpuFeatures, "vfpv3"))
    271                     g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
    272 
    273                 else if (has_list_item(cpuFeatures, "vfpv3d16"))
    274                     g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
    275 
    276                 if (has_list_item(cpuFeatures, "neon")) {
    277                     /* Note: Certain kernels only report neon but not vfpv3
    278                      *       in their features list. However, ARM mandates
    279                      *       that if Neon is implemented, so must be VFPv3
    280                      *       so always set the flag.
    281                      */
    282                     g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON |
    283                                      ANDROID_CPU_ARM_FEATURE_VFPv3;
    284                 }
    285                 free(cpuFeatures);
    286             }
    287         }
    288     }
    289 #endif /* __ARM_ARCH__ */
    290 
    291 #ifdef __i386__
    292     g_cpuFamily = ANDROID_CPU_FAMILY_X86;
    293 #endif
    294 }
    295 
    296 
    297 AndroidCpuFamily
    298 android_getCpuFamily(void)
    299 {
    300     pthread_once(&g_once, android_cpuInit);
    301     return g_cpuFamily;
    302 }
    303 
    304 
    305 uint64_t
    306 android_getCpuFeatures(void)
    307 {
    308     pthread_once(&g_once, android_cpuInit);
    309     return g_cpuFeatures;
    310 }
    311