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