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