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