cpu-features.c revision a4c9549aee353ff35124cd39c521138540332f22
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
59static  pthread_once_t     g_once;
60static  AndroidCpuFamily   g_cpuFamily;
61static  uint64_t           g_cpuFeatures;
62static  int                g_cpuCount;
63
64static 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__
82static __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 */
107static int
108read_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 */
131static char*
132extract_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
176EXIT:
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 */
187static int
188has_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 */
227static const char*
228parse_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 */
252typedef struct {
253    uint32_t mask;
254} CpuList;
255
256static __inline__ void
257cpulist_init(CpuList* list) {
258    list->mask = 0;
259}
260
261static __inline__ void
262cpulist_and(CpuList* list1, CpuList* list2) {
263    list1->mask &= list2->mask;
264}
265
266static __inline__ void
267cpulist_set(CpuList* list, int index) {
268    if ((unsigned)index < 32) {
269        list->mask |= (uint32_t)(1U << index);
270    }
271}
272
273static __inline__ int
274cpulist_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 */
288static void
289cpulist_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
335BAD_FORMAT:
336    ;
337}
338
339/* Read a CPU list from one sysfs file */
340static void
341cpulist_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 */
363static int
364get_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
380static void
381android_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 architecture");
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, "Features");
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
529
530AndroidCpuFamily
531android_getCpuFamily(void)
532{
533    pthread_once(&g_once, android_cpuInit);
534    return g_cpuFamily;
535}
536
537
538uint64_t
539android_getCpuFeatures(void)
540{
541    pthread_once(&g_once, android_cpuInit);
542    return g_cpuFeatures;
543}
544
545
546int
547android_getCpuCount(void)
548{
549    pthread_once(&g_once, android_cpuInit);
550    return g_cpuCount;
551}
552