OLD | NEW |
(Empty) | |
| 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 r7c: Fix CPU count computation. The old method only reported the |
| 32 * number of _active_ CPUs when the library was initialized, |
| 33 * which could be less than the real total. |
| 34 * |
| 35 * NDK r5: Handle buggy kernels which report a CPU Architecture number of 7 |
| 36 * for an ARMv6 CPU (see below). |
| 37 * |
| 38 * Handle kernels that only report 'neon', and not 'vfpv3' |
| 39 * (VFPv3 is mandated by the ARM architecture is Neon is implemented) |
| 40 * |
| 41 * Handle kernels that only report 'vfpv3d16', and not 'vfpv3' |
| 42 * |
| 43 * Fix x86 compilation. Report ANDROID_CPU_FAMILY_X86 in |
| 44 * android_getCpuFamily(). |
| 45 * |
| 46 * NDK r4: Initial release |
| 47 */ |
| 48 #include <sys/system_properties.h> |
| 49 #ifdef __arm__ |
| 50 #include <machine/cpu-features.h> |
| 51 #endif |
| 52 #include <pthread.h> |
| 53 #include "cpu-features.h" |
| 54 #include <stdio.h> |
| 55 #include <stdlib.h> |
| 56 #include <fcntl.h> |
| 57 #include <errno.h> |
| 58 |
| 59 static pthread_once_t g_once; |
| 60 static AndroidCpuFamily g_cpuFamily; |
| 61 static uint64_t g_cpuFeatures; |
| 62 static int g_cpuCount; |
| 63 |
| 64 static const int android_cpufeatures_debug = 0; |
| 65 |
| 66 #ifdef __arm__ |
| 67 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM |
| 68 #elif defined __i386__ |
| 69 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86 |
| 70 #else |
| 71 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN |
| 72 #endif |
| 73 |
| 74 #define D(...) \ |
| 75 do { \ |
| 76 if (android_cpufeatures_debug) { \ |
| 77 printf(__VA_ARGS__); fflush(stdout); \ |
| 78 } \ |
| 79 } while (0) |
| 80 |
| 81 #ifdef __i386__ |
| 82 static __inline__ void x86_cpuid(int func, int values[4]) |
| 83 { |
| 84 int a, b, c, d; |
| 85 /* We need to preserve ebx since we're compiling PIC code */ |
| 86 /* this means we can't use "=b" for the second output register */ |
| 87 __asm__ __volatile__ ( \ |
| 88 "push %%ebx\n" |
| 89 "cpuid\n" \ |
| 90 "mov %1, %%ebx\n" |
| 91 "pop %%ebx\n" |
| 92 : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ |
| 93 : "a" (func) \ |
| 94 ); |
| 95 values[0] = a; |
| 96 values[1] = b; |
| 97 values[2] = c; |
| 98 values[3] = d; |
| 99 } |
| 100 #endif |
| 101 |
| 102 /* Read the content of /proc/cpuinfo into a user-provided buffer. |
| 103 * Return the length of the data, or -1 on error. Does *not* |
| 104 * zero-terminate the content. Will not read more |
| 105 * than 'buffsize' bytes. |
| 106 */ |
| 107 static int |
| 108 read_file(const char* pathname, char* buffer, size_t buffsize) |
| 109 { |
| 110 int fd, len; |
| 111 |
| 112 fd = open(pathname, O_RDONLY); |
| 113 if (fd < 0) |
| 114 return -1; |
| 115 |
| 116 do { |
| 117 len = read(fd, buffer, buffsize); |
| 118 } while (len < 0 && errno == EINTR); |
| 119 |
| 120 close(fd); |
| 121 |
| 122 return len; |
| 123 } |
| 124 |
| 125 /* Extract the content of a the first occurence of a given field in |
| 126 * the content of /proc/cpuinfo and return it as a heap-allocated |
| 127 * string that must be freed by the caller. |
| 128 * |
| 129 * Return NULL if not found |
| 130 */ |
| 131 static char* |
| 132 extract_cpuinfo_field(char* buffer, int buflen, const char* field) |
| 133 { |
| 134 int fieldlen = strlen(field); |
| 135 char* bufend = buffer + buflen; |
| 136 char* result = NULL; |
| 137 int len, ignore; |
| 138 const char *p, *q; |
| 139 |
| 140 /* Look for first field occurence, and ensures it starts the line. |
| 141 */ |
| 142 p = buffer; |
| 143 bufend = buffer + buflen; |
| 144 for (;;) { |
| 145 p = memmem(p, bufend-p, field, fieldlen); |
| 146 if (p == NULL) |
| 147 goto EXIT; |
| 148 |
| 149 if (p == buffer || p[-1] == '\n') |
| 150 break; |
| 151 |
| 152 p += fieldlen; |
| 153 } |
| 154 |
| 155 /* Skip to the first column followed by a space */ |
| 156 p += fieldlen; |
| 157 p = memchr(p, ':', bufend-p); |
| 158 if (p == NULL || p[1] != ' ') |
| 159 goto EXIT; |
| 160 |
| 161 /* Find the end of the line */ |
| 162 p += 2; |
| 163 q = memchr(p, '\n', bufend-p); |
| 164 if (q == NULL) |
| 165 q = bufend; |
| 166 |
| 167 /* Copy the line into a heap-allocated buffer */ |
| 168 len = q-p; |
| 169 result = malloc(len+1); |
| 170 if (result == NULL) |
| 171 goto EXIT; |
| 172 |
| 173 memcpy(result, p, len); |
| 174 result[len] = '\0'; |
| 175 |
| 176 EXIT: |
| 177 return result; |
| 178 } |
| 179 |
| 180 /* Like strlen(), but for constant string literals */ |
| 181 #define STRLEN_CONST(x) ((sizeof(x)-1) |
| 182 |
| 183 |
| 184 /* Checks that a space-separated list of items contains one given 'item'. |
| 185 * Returns 1 if found, 0 otherwise. |
| 186 */ |
| 187 static int |
| 188 has_list_item(const char* list, const char* item) |
| 189 { |
| 190 const char* p = list; |
| 191 int itemlen = strlen(item); |
| 192 |
| 193 if (list == NULL) |
| 194 return 0; |
| 195 |
| 196 while (*p) { |
| 197 const char* q; |
| 198 |
| 199 /* skip spaces */ |
| 200 while (*p == ' ' || *p == '\t') |
| 201 p++; |
| 202 |
| 203 /* find end of current list item */ |
| 204 q = p; |
| 205 while (*q && *q != ' ' && *q != '\t') |
| 206 q++; |
| 207 |
| 208 if (itemlen == q-p && !memcmp(p, item, itemlen)) |
| 209 return 1; |
| 210 |
| 211 /* skip to next item */ |
| 212 p = q; |
| 213 } |
| 214 return 0; |
| 215 } |
| 216 |
| 217 /* Parse an decimal integer starting from 'input', but not going further |
| 218 * than 'limit'. Return the value into '*result'. |
| 219 * |
| 220 * NOTE: Does not skip over leading spaces, or deal with sign characters. |
| 221 * NOTE: Ignores overflows. |
| 222 * |
| 223 * The function returns NULL in case of error (bad format), or the new |
| 224 * position after the decimal number in case of success (which will always |
| 225 * be <= 'limit'). |
| 226 */ |
| 227 static const char* |
| 228 parse_decimal(const char* input, const char* limit, int* result) |
| 229 { |
| 230 const char* p = input; |
| 231 int val = 0; |
| 232 while (p < limit) { |
| 233 int d = (*p - '0'); |
| 234 if ((unsigned)d >= 10U) |
| 235 break; |
| 236 val = val*10 + d; |
| 237 p++; |
| 238 } |
| 239 if (p == input) |
| 240 return NULL; |
| 241 |
| 242 *result = val; |
| 243 return p; |
| 244 } |
| 245 |
| 246 /* This small data type is used to represent a CPU list / mask, as read |
| 247 * from sysfs on Linux. See http://www.kernel.org/doc/Documentation/cputopology.
txt |
| 248 * |
| 249 * For now, we don't expect more than 32 cores on mobile devices, so keep |
| 250 * everything simple. |
| 251 */ |
| 252 typedef struct { |
| 253 uint32_t mask; |
| 254 } CpuList; |
| 255 |
| 256 static __inline__ void |
| 257 cpulist_init(CpuList* list) { |
| 258 list->mask = 0; |
| 259 } |
| 260 |
| 261 static __inline__ void |
| 262 cpulist_and(CpuList* list1, CpuList* list2) { |
| 263 list1->mask &= list2->mask; |
| 264 } |
| 265 |
| 266 static __inline__ void |
| 267 cpulist_set(CpuList* list, int index) { |
| 268 if ((unsigned)index < 32) { |
| 269 list->mask |= (uint32_t)(1U << index); |
| 270 } |
| 271 } |
| 272 |
| 273 static __inline__ int |
| 274 cpulist_count(CpuList* list) { |
| 275 return __builtin_popcount(list->mask); |
| 276 } |
| 277 |
| 278 /* Parse a textual list of cpus and store the result inside a CpuList object. |
| 279 * Input format is the following: |
| 280 * - comma-separated list of items (no spaces) |
| 281 * - each item is either a single decimal number (cpu index), or a range made |
| 282 * of two numbers separated by a single dash (-). Ranges are inclusive. |
| 283 * |
| 284 * Examples: 0 |
| 285 * 2,4-127,128-143 |
| 286 * 0-1 |
| 287 */ |
| 288 static void |
| 289 cpulist_parse(CpuList* list, const char* line, int line_len) |
| 290 { |
| 291 const char* p = line; |
| 292 const char* end = p + line_len; |
| 293 const char* q; |
| 294 |
| 295 /* NOTE: the input line coming from sysfs typically contains a |
| 296 * trailing newline, so take care of it in the code below |
| 297 */ |
| 298 while (p < end && *p != '\n') |
| 299 { |
| 300 int val, start_value, end_value; |
| 301 |
| 302 /* Find the end of current item, and put it into 'q' */ |
| 303 q = memchr(p, ',', end-p); |
| 304 if (q == NULL) { |
| 305 q = end; |
| 306 } |
| 307 |
| 308 /* Get first value */ |
| 309 p = parse_decimal(p, q, &start_value); |
| 310 if (p == NULL) |
| 311 goto BAD_FORMAT; |
| 312 |
| 313 end_value = start_value; |
| 314 |
| 315 /* If we're not at the end of the item, expect a dash and |
| 316 * and integer; extract end value. |
| 317 */ |
| 318 if (p < q && *p == '-') { |
| 319 p = parse_decimal(p+1, q, &end_value); |
| 320 if (p == NULL) |
| 321 goto BAD_FORMAT; |
| 322 } |
| 323 |
| 324 /* Set bits CPU list bits */ |
| 325 for (val = start_value; val <= end_value; val++) { |
| 326 cpulist_set(list, val); |
| 327 } |
| 328 |
| 329 /* Jump to next item */ |
| 330 p = q; |
| 331 if (p < end) |
| 332 p++; |
| 333 } |
| 334 |
| 335 BAD_FORMAT: |
| 336 ; |
| 337 } |
| 338 |
| 339 /* Read a CPU list from one sysfs file */ |
| 340 static void |
| 341 cpulist_read_from(CpuList* list, const char* filename) |
| 342 { |
| 343 char file[64]; |
| 344 int filelen; |
| 345 |
| 346 cpulist_init(list); |
| 347 |
| 348 filelen = read_file(filename, file, sizeof file); |
| 349 if (filelen < 0) { |
| 350 D("Could not read %s: %s\n", filename, strerror(errno)); |
| 351 return; |
| 352 } |
| 353 |
| 354 cpulist_parse(list, file, filelen); |
| 355 } |
| 356 |
| 357 /* Return the number of cpus present on a given device. |
| 358 * |
| 359 * To handle all weird kernel configurations, we need to compute the |
| 360 * intersection of the 'present' and 'possible' CPU lists and count |
| 361 * the result. |
| 362 */ |
| 363 static int |
| 364 get_cpu_count(void) |
| 365 { |
| 366 CpuList cpus_present[1]; |
| 367 CpuList cpus_possible[1]; |
| 368 |
| 369 cpulist_read_from(cpus_present, "/sys/devices/system/cpu/present"); |
| 370 cpulist_read_from(cpus_possible, "/sys/devices/system/cpu/possible"); |
| 371 |
| 372 /* Compute the intersection of both sets to get the actual number of |
| 373 * CPU cores that can be used on this device by the kernel. |
| 374 */ |
| 375 cpulist_and(cpus_present, cpus_possible); |
| 376 |
| 377 return cpulist_count(cpus_present); |
| 378 } |
| 379 |
| 380 static void |
| 381 android_cpuInit(void) |
| 382 { |
| 383 char cpuinfo[4096]; |
| 384 int cpuinfo_len; |
| 385 |
| 386 g_cpuFamily = DEFAULT_CPU_FAMILY; |
| 387 g_cpuFeatures = 0; |
| 388 g_cpuCount = 1; |
| 389 |
| 390 cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo); |
| 391 D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, |
| 392 cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo); |
| 393 |
| 394 if (cpuinfo_len < 0) /* should not happen */ { |
| 395 return; |
| 396 } |
| 397 |
| 398 /* Count the CPU cores, the value may be 0 for single-core CPUs */ |
| 399 g_cpuCount = get_cpu_count(); |
| 400 if (g_cpuCount == 0) { |
| 401 g_cpuCount = 1; |
| 402 } |
| 403 |
| 404 D("found cpuCount = %d\n", g_cpuCount); |
| 405 |
| 406 #ifdef __ARM_ARCH__ |
| 407 { |
| 408 char* features = NULL; |
| 409 char* architecture = NULL; |
| 410 |
| 411 /* Extract architecture from the "CPU Architecture" field. |
| 412 * The list is well-known, unlike the the output of |
| 413 * the 'Processor' field which can vary greatly. |
| 414 * |
| 415 * See the definition of the 'proc_arch' array in |
| 416 * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in |
| 417 * same file. |
| 418 */ |
| 419 char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU archite
cture"); |
| 420 |
| 421 if (cpuArch != NULL) { |
| 422 char* end; |
| 423 long archNumber; |
| 424 int hasARMv7 = 0; |
| 425 |
| 426 D("found cpuArch = '%s'\n", cpuArch); |
| 427 |
| 428 /* read the initial decimal number, ignore the rest */ |
| 429 archNumber = strtol(cpuArch, &end, 10); |
| 430 |
| 431 /* Here we assume that ARMv8 will be upwards compatible with v7 |
| 432 * in the future. Unfortunately, there is no 'Features' field to |
| 433 * indicate that Thumb-2 is supported. |
| 434 */ |
| 435 if (end > cpuArch && archNumber >= 7) { |
| 436 hasARMv7 = 1; |
| 437 } |
| 438 |
| 439 /* Unfortunately, it seems that certain ARMv6-based CPUs |
| 440 * report an incorrect architecture number of 7! |
| 441 * |
| 442 * See http://code.google.com/p/android/issues/detail?id=10812 |
| 443 * |
| 444 * We try to correct this by looking at the 'elf_format' |
| 445 * field reported by the 'Processor' field, which is of the |
| 446 * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for |
| 447 * an ARMv6-one. |
| 448 */ |
| 449 if (hasARMv7) { |
| 450 char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len, |
| 451 "Processor"); |
| 452 if (cpuProc != NULL) { |
| 453 D("found cpuProc = '%s'\n", cpuProc); |
| 454 if (has_list_item(cpuProc, "(v6l)")) { |
| 455 D("CPU processor and architecture mismatch!!\n"); |
| 456 hasARMv7 = 0; |
| 457 } |
| 458 free(cpuProc); |
| 459 } |
| 460 } |
| 461 |
| 462 if (hasARMv7) { |
| 463 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7; |
| 464 } |
| 465 |
| 466 /* The LDREX / STREX instructions are available from ARMv6 */ |
| 467 if (archNumber >= 6) { |
| 468 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX; |
| 469 } |
| 470 |
| 471 free(cpuArch); |
| 472 } |
| 473 |
| 474 /* Extract the list of CPU features from 'Features' field */ |
| 475 char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Feature
s"); |
| 476 |
| 477 if (cpuFeatures != NULL) { |
| 478 |
| 479 D("found cpuFeatures = '%s'\n", cpuFeatures); |
| 480 |
| 481 if (has_list_item(cpuFeatures, "vfpv3")) |
| 482 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; |
| 483 |
| 484 else if (has_list_item(cpuFeatures, "vfpv3d16")) |
| 485 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; |
| 486 |
| 487 if (has_list_item(cpuFeatures, "neon")) { |
| 488 /* Note: Certain kernels only report neon but not vfpv3 |
| 489 * in their features list. However, ARM mandates |
| 490 * that if Neon is implemented, so must be VFPv3 |
| 491 * so always set the flag. |
| 492 */ |
| 493 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON | |
| 494 ANDROID_CPU_ARM_FEATURE_VFPv3; |
| 495 } |
| 496 free(cpuFeatures); |
| 497 } |
| 498 } |
| 499 #endif /* __ARM_ARCH__ */ |
| 500 |
| 501 #ifdef __i386__ |
| 502 g_cpuFamily = ANDROID_CPU_FAMILY_X86; |
| 503 |
| 504 int regs[4]; |
| 505 |
| 506 /* According to http://en.wikipedia.org/wiki/CPUID */ |
| 507 #define VENDOR_INTEL_b 0x756e6547 |
| 508 #define VENDOR_INTEL_c 0x6c65746e |
| 509 #define VENDOR_INTEL_d 0x49656e69 |
| 510 |
| 511 x86_cpuid(0, regs); |
| 512 int vendorIsIntel = (regs[1] == VENDOR_INTEL_b && |
| 513 regs[2] == VENDOR_INTEL_c && |
| 514 regs[3] == VENDOR_INTEL_d); |
| 515 |
| 516 x86_cpuid(1, regs); |
| 517 if ((regs[2] & (1 << 9)) != 0) { |
| 518 g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3; |
| 519 } |
| 520 if ((regs[2] & (1 << 23)) != 0) { |
| 521 g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT; |
| 522 } |
| 523 if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) { |
| 524 g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE; |
| 525 } |
| 526 #endif |
| 527 |
| 528 #ifdef _MIPS_ARCH |
| 529 g_cpuFamily = ANDROID_CPU_FAMILY_MIPS; |
| 530 #endif /* _MIPS_ARCH */ |
| 531 } |
| 532 |
| 533 |
| 534 AndroidCpuFamily |
| 535 android_getCpuFamily(void) |
| 536 { |
| 537 pthread_once(&g_once, android_cpuInit); |
| 538 return g_cpuFamily; |
| 539 } |
| 540 |
| 541 |
| 542 uint64_t |
| 543 android_getCpuFeatures(void) |
| 544 { |
| 545 pthread_once(&g_once, android_cpuInit); |
| 546 return g_cpuFeatures; |
| 547 } |
| 548 |
| 549 |
| 550 int |
| 551 android_getCpuCount(void) |
| 552 { |
| 553 pthread_once(&g_once, android_cpuInit); |
| 554 return g_cpuCount; |
| 555 } |
OLD | NEW |