1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <sys/system_properties.h> 12 #ifdef __arm__ 13 #include <machine/cpu-features.h> 14 #endif 15 #include <pthread.h> 16 #include "cpu-features.h" 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <fcntl.h> 20 #include <errno.h> 21 22 static pthread_once_t g_once; 23 static AndroidCpuFamily g_cpuFamily; 24 static uint64_t g_cpuFeatures; 25 static int g_cpuCount; 26 27 static const int android_cpufeatures_debug = 0; 28 29 #ifdef __arm__ 30 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM 31 #elif defined __i386__ 32 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86 33 #else 34 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN 35 #endif 36 37 #define D(...) \ 38 do { \ 39 if (android_cpufeatures_debug) { \ 40 printf(__VA_ARGS__); fflush(stdout); \ 41 } \ 42 } while (0) 43 44 #ifdef __i386__ 45 static __inline__ void x86_cpuid(int func, int values[4]) 46 { 47 int a, b, c, d; 48 /* We need to preserve ebx since we're compiling PIC code */ 49 /* this means we can't use "=b" for the second output register */ 50 __asm__ __volatile__ ( \ 51 "push %%ebx\n" 52 "cpuid\n" \ 53 "mov %1, %%ebx\n" 54 "pop %%ebx\n" 55 : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ 56 : "a" (func) \ 57 ); 58 values[0] = a; 59 values[1] = b; 60 values[2] = c; 61 values[3] = d; 62 } 63 #endif 64 65 /* Read the content of /proc/cpuinfo into a user-provided buffer. 66 * Return the length of the data, or -1 on error. Does *not* 67 * zero-terminate the content. Will not read more 68 * than 'buffsize' bytes. 69 */ 70 static int 71 read_file(const char* pathname, char* buffer, size_t buffsize) 72 { 73 int fd, len; 74 75 fd = open(pathname, O_RDONLY); 76 if (fd < 0) 77 return -1; 78 79 do { 80 len = read(fd, buffer, buffsize); 81 } while (len < 0 && errno == EINTR); 82 83 close(fd); 84 85 return len; 86 } 87 88 /* Extract the content of a the first occurence of a given field in 89 * the content of /proc/cpuinfo and return it as a heap-allocated 90 * string that must be freed by the caller. 91 * 92 * Return NULL if not found 93 */ 94 static char* 95 extract_cpuinfo_field(char* buffer, int buflen, const char* field) 96 { 97 int fieldlen = strlen(field); 98 char* bufend = buffer + buflen; 99 char* result = NULL; 100 int len, ignore; 101 const char *p, *q; 102 103 /* Look for first field occurence, and ensures it starts the line. 104 */ 105 p = buffer; 106 bufend = buffer + buflen; 107 for (;;) { 108 p = memmem(p, bufend-p, field, fieldlen); 109 if (p == NULL) 110 goto EXIT; 111 112 if (p == buffer || p[-1] == '\n') 113 break; 114 115 p += fieldlen; 116 } 117 118 /* Skip to the first column followed by a space */ 119 p += fieldlen; 120 p = memchr(p, ':', bufend-p); 121 if (p == NULL || p[1] != ' ') 122 goto EXIT; 123 124 /* Find the end of the line */ 125 p += 2; 126 q = memchr(p, '\n', bufend-p); 127 if (q == NULL) 128 q = bufend; 129 130 /* Copy the line into a heap-allocated buffer */ 131 len = q-p; 132 result = malloc(len+1); 133 if (result == NULL) 134 goto EXIT; 135 136 memcpy(result, p, len); 137 result[len] = '\0'; 138 139 EXIT: 140 return result; 141 } 142 143 /* Count the number of occurences of a given field prefix in /proc/cpuinfo. 144 */ 145 static int 146 count_cpuinfo_field(char* buffer, int buflen, const char* field) 147 { 148 int fieldlen = strlen(field); 149 const char* p = buffer; 150 const char* bufend = buffer + buflen; 151 const char* q; 152 int count = 0; 153 154 for (;;) { 155 const char* q; 156 157 p = memmem(p, bufend-p, field, fieldlen); 158 if (p == NULL) 159 break; 160 161 /* Ensure that the field is at the start of a line */ 162 if (p > buffer && p[-1] != '\n') { 163 p += fieldlen; 164 continue; 165 } 166 167 168 /* skip any whitespace */ 169 q = p + fieldlen; 170 while (q < bufend && (*q == ' ' || *q == '\t')) 171 q++; 172 173 /* we must have a colon now */ 174 if (q < bufend && *q == ':') { 175 count += 1; 176 q ++; 177 } 178 p = q; 179 } 180 181 return count; 182 } 183 184 /* Like strlen(), but for constant string literals */ 185 #define STRLEN_CONST(x) ((sizeof(x)-1) 186 187 188 /* Checks that a space-separated list of items contains one given 'item'. 189 * Returns 1 if found, 0 otherwise. 190 */ 191 static int 192 has_list_item(const char* list, const char* item) 193 { 194 const char* p = list; 195 int itemlen = strlen(item); 196 197 if (list == NULL) 198 return 0; 199 200 while (*p) { 201 const char* q; 202 203 /* skip spaces */ 204 while (*p == ' ' || *p == '\t') 205 p++; 206 207 /* find end of current list item */ 208 q = p; 209 while (*q && *q != ' ' && *q != '\t') 210 q++; 211 212 if (itemlen == q-p && !memcmp(p, item, itemlen)) 213 return 1; 214 215 /* skip to next item */ 216 p = q; 217 } 218 return 0; 219 } 220 221 222 static void 223 android_cpuInit(void) 224 { 225 char cpuinfo[4096]; 226 int cpuinfo_len; 227 228 g_cpuFamily = DEFAULT_CPU_FAMILY; 229 g_cpuFeatures = 0; 230 g_cpuCount = 1; 231 232 cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo); 233 D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, 234 cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo); 235 236 if (cpuinfo_len < 0) /* should not happen */ { 237 return; 238 } 239 240 /* Count the CPU cores, the value may be 0 for single-core CPUs */ 241 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor"); 242 if (g_cpuCount == 0) { 243 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor"); 244 if (g_cpuCount == 0) { 245 g_cpuCount = 1; 246 } 247 } 248 249 D("found cpuCount = %d\n", g_cpuCount); 250 251 #ifdef __ARM_ARCH__ 252 { 253 char* features = NULL; 254 char* architecture = NULL; 255 256 /* Extract architecture from the "CPU Architecture" field. 257 * The list is well-known, unlike the the output of 258 * the 'Processor' field which can vary greatly. 259 * 260 * See the definition of the 'proc_arch' array in 261 * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in 262 * same file. 263 */ 264 char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture"); 265 266 if (cpuArch != NULL) { 267 char* end; 268 long archNumber; 269 int hasARMv7 = 0; 270 271 D("found cpuArch = '%s'\n", cpuArch); 272 273 /* read the initial decimal number, ignore the rest */ 274 archNumber = strtol(cpuArch, &end, 10); 275 276 /* Here we assume that ARMv8 will be upwards compatible with v7 277 * in the future. Unfortunately, there is no 'Features' field to 278 * indicate that Thumb-2 is supported. 279 */ 280 if (end > cpuArch && archNumber >= 7) { 281 hasARMv7 = 1; 282 } 283 284 /* Unfortunately, it seems that certain ARMv6-based CPUs 285 * report an incorrect architecture number of 7! 286 * 287 * See http://code.google.com/p/android/issues/detail?id=10812 288 * 289 * We try to correct this by looking at the 'elf_format' 290 * field reported by the 'Processor' field, which is of the 291 * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for 292 * an ARMv6-one. 293 */ 294 if (hasARMv7) { 295 char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len, 296 "Processor"); 297 if (cpuProc != NULL) { 298 D("found cpuProc = '%s'\n", cpuProc); 299 if (has_list_item(cpuProc, "(v6l)")) { 300 D("CPU processor and architecture mismatch!!\n"); 301 hasARMv7 = 0; 302 } 303 free(cpuProc); 304 } 305 } 306 307 if (hasARMv7) { 308 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7; 309 } 310 311 /* The LDREX / STREX instructions are available from ARMv6 */ 312 if (archNumber >= 6) { 313 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX; 314 } 315 316 free(cpuArch); 317 } 318 319 /* Extract the list of CPU features from 'Features' field */ 320 char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features"); 321 322 if (cpuFeatures != NULL) { 323 324 D("found cpuFeatures = '%s'\n", cpuFeatures); 325 326 if (has_list_item(cpuFeatures, "vfpv3")) 327 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; 328 329 else if (has_list_item(cpuFeatures, "vfpv3d16")) 330 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3; 331 332 if (has_list_item(cpuFeatures, "neon")) { 333 /* Note: Certain kernels only report neon but not vfpv3 334 * in their features list. However, ARM mandates 335 * that if Neon is implemented, so must be VFPv3 336 * so always set the flag. 337 */ 338 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON | 339 ANDROID_CPU_ARM_FEATURE_VFPv3; 340 } 341 free(cpuFeatures); 342 } 343 } 344 #endif /* __ARM_ARCH__ */ 345 346 #ifdef __i386__ 347 g_cpuFamily = ANDROID_CPU_FAMILY_X86; 348 349 int regs[4]; 350 351 /* According to http://en.wikipedia.org/wiki/CPUID */ 352 #define VENDOR_INTEL_b 0x756e6547 353 #define VENDOR_INTEL_c 0x6c65746e 354 #define VENDOR_INTEL_d 0x49656e69 355 356 x86_cpuid(0, regs); 357 int vendorIsIntel = (regs[1] == VENDOR_INTEL_b && 358 regs[2] == VENDOR_INTEL_c && 359 regs[3] == VENDOR_INTEL_d); 360 361 x86_cpuid(1, regs); 362 if ((regs[2] & (1 << 9)) != 0) { 363 g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_SSSE3; 364 } 365 if ((regs[2] & (1 << 23)) != 0) { 366 g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_POPCNT; 367 } 368 if (vendorIsIntel && (regs[2] & (1 << 22)) != 0) { 369 g_cpuFeatures |= ANDROID_CPU_X86_FEATURE_MOVBE; 370 } 371 #endif 372 } 373 374 375 AndroidCpuFamily 376 android_getCpuFamily(void) 377 { 378 pthread_once(&g_once, android_cpuInit); 379 return g_cpuFamily; 380 } 381 382 383 uint64_t 384 android_getCpuFeatures(void) 385 { 386 pthread_once(&g_once, android_cpuInit); 387 return g_cpuFeatures; 388 } 389 390 391 int 392 android_getCpuCount(void) 393 { 394 pthread_once(&g_once, android_cpuInit); 395 return g_cpuCount; 396 } 397