1 /* 2 * Copyright (c) 2011 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 // This file is derived from Android's NDK package r7, located at 12 // <ndk>/sources/android/cpufeatures/ (downloadable from 13 // http://developer.android.com/sdk/ndk/index.html). 14 15 #include "cpu_features_wrapper.h" 16 17 #include <fcntl.h> 18 #include <errno.h> 19 #include <pthread.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 23 // Define CPU family. 24 typedef enum { 25 CPU_FAMILY_UNKNOWN = 0, 26 CPU_FAMILY_ARM, 27 CPU_FAMILY_X86, 28 CPU_FAMILY_MAX // Do not remove. 29 } CpuFamily; 30 31 static pthread_once_t g_once; 32 static CpuFamily g_cpuFamily; 33 static uint64_t g_cpuFeatures; 34 static int g_cpuCount; 35 36 static const int cpufeatures_debug = 0; 37 38 #ifdef __arm__ 39 # define DEFAULT_CPU_FAMILY CPU_FAMILY_ARM 40 #elif defined __i386__ 41 # define DEFAULT_CPU_FAMILY CPU_FAMILY_X86 42 #else 43 # define DEFAULT_CPU_FAMILY CPU_FAMILY_UNKNOWN 44 #endif 45 46 #define D(...) \ 47 do { \ 48 if (cpufeatures_debug) { \ 49 printf(__VA_ARGS__); fflush(stdout); \ 50 } \ 51 } while (0) 52 53 /* Read the content of /proc/cpuinfo into a user-provided buffer. 54 * Return the length of the data, or -1 on error. Does *not* 55 * zero-terminate the content. Will not read more 56 * than 'buffsize' bytes. 57 */ 58 static int read_file(const char* pathname, char* buffer, size_t buffsize) { 59 int fd, len; 60 61 fd = open(pathname, O_RDONLY); 62 if (fd < 0) 63 return -1; 64 65 do { 66 len = read(fd, buffer, buffsize); 67 } while (len < 0 && errno == EINTR); 68 69 close(fd); 70 71 return len; 72 } 73 74 /* Extract the content of a the first occurence of a given field in 75 * the content of /proc/cpuinfo and return it as a heap-allocated 76 * string that must be freed by the caller. 77 * 78 * Return NULL if not found 79 */ 80 static char* extract_cpuinfo_field(char* buffer, int buflen, const char* field) { 81 int fieldlen = strlen(field); 82 char* bufend = buffer + buflen; 83 char* result = NULL; 84 int len, ignore; 85 const char* p, *q; 86 87 /* Look for first field occurence, and ensures it starts the line. 88 */ 89 p = buffer; 90 bufend = buffer + buflen; 91 for (;;) { 92 p = memmem(p, bufend - p, field, fieldlen); 93 if (p == NULL) 94 goto EXIT; 95 96 if (p == buffer || p[-1] == '\n') 97 break; 98 99 p += fieldlen; 100 } 101 102 /* Skip to the first column followed by a space */ 103 p += fieldlen; 104 p = memchr(p, ':', bufend - p); 105 if (p == NULL || p[1] != ' ') 106 goto EXIT; 107 108 /* Find the end of the line */ 109 p += 2; 110 q = memchr(p, '\n', bufend - p); 111 if (q == NULL) 112 q = bufend; 113 114 /* Copy the line into a heap-allocated buffer */ 115 len = q - p; 116 result = malloc(len + 1); 117 if (result == NULL) 118 goto EXIT; 119 120 memcpy(result, p, len); 121 result[len] = '\0'; 122 123 EXIT: 124 return result; 125 } 126 127 /* Count the number of occurences of a given field prefix in /proc/cpuinfo. 128 */ 129 static int count_cpuinfo_field(char* buffer, int buflen, const char* field) { 130 int fieldlen = strlen(field); 131 const char* p = buffer; 132 const char* bufend = buffer + buflen; 133 const char* q; 134 int count = 0; 135 136 for (;;) { 137 const char* q; 138 139 p = memmem(p, bufend - p, field, fieldlen); 140 if (p == NULL) 141 break; 142 143 /* Ensure that the field is at the start of a line */ 144 if (p > buffer && p[-1] != '\n') { 145 p += fieldlen; 146 continue; 147 } 148 149 150 /* skip any whitespace */ 151 q = p + fieldlen; 152 while (q < bufend && (*q == ' ' || *q == '\t')) 153 q++; 154 155 /* we must have a colon now */ 156 if (q < bufend && *q == ':') { 157 count += 1; 158 q ++; 159 } 160 p = q; 161 } 162 163 return count; 164 } 165 166 /* Like strlen(), but for constant string literals */ 167 #define STRLEN_CONST(x) ((sizeof(x)-1) 168 169 170 /* Checks that a space-separated list of items contains one given 'item'. 171 * Returns 1 if found, 0 otherwise. 172 */ 173 static int has_list_item(const char* list, const char* item) { 174 const char* p = list; 175 int itemlen = strlen(item); 176 177 if (list == NULL) 178 return 0; 179 180 while (*p) { 181 const char* q; 182 183 /* skip spaces */ 184 while (*p == ' ' || *p == '\t') 185 p++; 186 187 /* find end of current list item */ 188 q = p; 189 while (*q && *q != ' ' && *q != '\t') 190 q++; 191 192 if (itemlen == q - p && !memcmp(p, item, itemlen)) 193 return 1; 194 195 /* skip to next item */ 196 p = q; 197 } 198 return 0; 199 } 200 201 202 static void cpuInit(void) { 203 char cpuinfo[4096]; 204 int cpuinfo_len; 205 206 g_cpuFamily = DEFAULT_CPU_FAMILY; 207 g_cpuFeatures = 0; 208 g_cpuCount = 1; 209 210 cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo); 211 D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, 212 cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo); 213 214 if (cpuinfo_len < 0) { /* should not happen */ 215 return; 216 } 217 218 /* Count the CPU cores, the value may be 0 for single-core CPUs */ 219 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor"); 220 if (g_cpuCount == 0) { 221 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor"); 222 if (g_cpuCount == 0) { 223 g_cpuCount = 1; 224 } 225 } 226 227 D("found cpuCount = %d\n", g_cpuCount); 228 229 #ifdef __arm__ 230 { 231 char* features = NULL; 232 char* architecture = NULL; 233 234 /* Extract architecture from the "CPU Architecture" field. 235 * The list is well-known, unlike the the output of 236 * the 'Processor' field which can vary greatly. 237 * 238 * See the definition of the 'proc_arch' array in 239 * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in 240 * same file. 241 */ 242 char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, 243 "CPU architecture"); 244 245 if (cpuArch != NULL) { 246 char* end; 247 long archNumber; 248 int hasARMv7 = 0; 249 250 D("found cpuArch = '%s'\n", cpuArch); 251 252 /* read the initial decimal number, ignore the rest */ 253 archNumber = strtol(cpuArch, &end, 10); 254 255 /* Here we assume that ARMv8 will be upwards compatible with v7 256 * in the future. Unfortunately, there is no 'Features' field to 257 * indicate that Thumb-2 is supported. 258 */ 259 if (end > cpuArch && archNumber >= 7) { 260 hasARMv7 = 1; 261 } 262 263 /* Unfortunately, it seems that certain ARMv6-based CPUs 264 * report an incorrect architecture number of 7! 265 * 266 * We try to correct this by looking at the 'elf_format' 267 * field reported by the 'Processor' field, which is of the 268 * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for 269 * an ARMv6-one. 270 */ 271 if (hasARMv7) { 272 char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len, 273 "Processor"); 274 if (cpuProc != NULL) { 275 D("found cpuProc = '%s'\n", cpuProc); 276 if (has_list_item(cpuProc, "(v6l)")) { 277 D("CPU processor and architecture mismatch!!\n"); 278 hasARMv7 = 0; 279 } 280 free(cpuProc); 281 } 282 } 283 284 if (hasARMv7) { 285 g_cpuFeatures |= kCPUFeatureARMv7; 286 } 287 288 /* The LDREX / STREX instructions are available from ARMv6 */ 289 if (archNumber >= 6) { 290 g_cpuFeatures |= kCPUFeatureLDREXSTREX; 291 } 292 293 free(cpuArch); 294 } 295 296 /* Extract the list of CPU features from 'Features' field */ 297 char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, 298 "Features"); 299 300 if (cpuFeatures != NULL) { 301 302 D("found cpuFeatures = '%s'\n", cpuFeatures); 303 304 if (has_list_item(cpuFeatures, "vfpv3")) 305 g_cpuFeatures |= kCPUFeatureVFPv3; 306 307 else if (has_list_item(cpuFeatures, "vfpv3d16")) 308 g_cpuFeatures |= kCPUFeatureVFPv3; 309 310 if (has_list_item(cpuFeatures, "neon")) { 311 /* Note: Certain kernels only report neon but not vfpv3 312 * in their features list. However, ARM mandates 313 * that if Neon is implemented, so must be VFPv3 314 * so always set the flag. 315 */ 316 g_cpuFeatures |= kCPUFeatureNEON | 317 kCPUFeatureVFPv3; 318 } 319 free(cpuFeatures); 320 } 321 } 322 #endif // __arm__ 323 324 #ifdef __i386__ 325 g_cpuFamily = CPU_FAMILY_X86; 326 #endif 327 } 328 329 330 uint64_t WebRtc_GetCPUFeaturesARM(void) { 331 pthread_once(&g_once, cpuInit); 332 return g_cpuFeatures; 333 } 334