FS#12750 by Vanja Cvelbar - update Slovenian translation
[maemo-rb.git] / android / cpufeatures / cpu-features.c
blobb027ea4b5cdbc384f0ac67ecab83fc6009271028
1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 * All rights reserved.
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.
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.
29 /* ChangeLog for this library:
31 * NDK r5: Handle buggy kernels which report a CPU Architecture number of 7
32 * for an ARMv6 CPU (see below).
34 * Handle kernels that only report 'neon', and not 'vfpv3'
35 * (VFPv3 is mandated by the ARM architecture is Neon is implemented)
37 * Handle kernels that only report 'vfpv3d16', and not 'vfpv3'
39 * Fix x86 compilation. Report ANDROID_CPU_FAMILY_X86 in
40 * android_getCpuFamily().
42 * NDK r4: Initial release
44 #include <sys/system_properties.h>
45 #ifdef __arm__
46 #include <machine/cpu-features.h>
47 #endif
48 #include <pthread.h>
49 #include "cpu-features.h"
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <fcntl.h>
53 #include <errno.h>
55 static pthread_once_t g_once;
56 static AndroidCpuFamily g_cpuFamily;
57 static uint64_t g_cpuFeatures;
58 static int g_cpuCount;
60 static const int android_cpufeatures_debug = 0;
62 #ifdef __arm__
63 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_ARM
64 #elif defined __i386__
65 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_X86
66 #else
67 # define DEFAULT_CPU_FAMILY ANDROID_CPU_FAMILY_UNKNOWN
68 #endif
70 #define D(...) \
71 do { \
72 if (android_cpufeatures_debug) { \
73 printf(__VA_ARGS__); fflush(stdout); \
74 } \
75 } while (0)
77 /* Read the content of /proc/cpuinfo into a user-provided buffer.
78 * Return the length of the data, or -1 on error. Does *not*
79 * zero-terminate the content. Will not read more
80 * than 'buffsize' bytes.
82 static int
83 read_file(const char* pathname, char* buffer, size_t buffsize)
85 int fd, len;
87 fd = open(pathname, O_RDONLY);
88 if (fd < 0)
89 return -1;
91 do {
92 len = read(fd, buffer, buffsize);
93 } while (len < 0 && errno == EINTR);
95 close(fd);
97 return len;
100 /* Extract the content of a the first occurence of a given field in
101 * the content of /proc/cpuinfo and return it as a heap-allocated
102 * string that must be freed by the caller.
104 * Return NULL if not found
106 static char*
107 extract_cpuinfo_field(char* buffer, int buflen, const char* field)
109 int fieldlen = strlen(field);
110 char* bufend = buffer + buflen;
111 char* result = NULL;
112 int len, ignore;
113 const char *p, *q;
115 /* Look for first field occurence, and ensures it starts the line.
117 p = buffer;
118 bufend = buffer + buflen;
119 for (;;) {
120 p = memmem(p, bufend-p, field, fieldlen);
121 if (p == NULL)
122 goto EXIT;
124 if (p == buffer || p[-1] == '\n')
125 break;
127 p += fieldlen;
130 /* Skip to the first column followed by a space */
131 p += fieldlen;
132 p = memchr(p, ':', bufend-p);
133 if (p == NULL || p[1] != ' ')
134 goto EXIT;
136 /* Find the end of the line */
137 p += 2;
138 q = memchr(p, '\n', bufend-p);
139 if (q == NULL)
140 q = bufend;
142 /* Copy the line into a heap-allocated buffer */
143 len = q-p;
144 result = malloc(len+1);
145 if (result == NULL)
146 goto EXIT;
148 memcpy(result, p, len);
149 result[len] = '\0';
151 EXIT:
152 return result;
155 /* Count the number of occurences of a given field prefix in /proc/cpuinfo.
157 static int
158 count_cpuinfo_field(char* buffer, int buflen, const char* field)
160 int fieldlen = strlen(field);
161 const char* p = buffer;
162 const char* bufend = buffer + buflen;
163 const char* q;
164 int count = 0;
166 for (;;) {
167 const char* q;
169 p = memmem(p, bufend-p, field, fieldlen);
170 if (p == NULL)
171 break;
173 /* Ensure that the field is at the start of a line */
174 if (p > buffer && p[-1] != '\n') {
175 p += fieldlen;
176 continue;
180 /* skip any whitespace */
181 q = p + fieldlen;
182 while (q < bufend && (*q == ' ' || *q == '\t'))
183 q++;
185 /* we must have a colon now */
186 if (q < bufend && *q == ':') {
187 count += 1;
188 q ++;
190 p = q;
193 return count;
196 /* Like strlen(), but for constant string literals */
197 #define STRLEN_CONST(x) ((sizeof(x)-1)
200 /* Checks that a space-separated list of items contains one given 'item'.
201 * Returns 1 if found, 0 otherwise.
203 static int
204 has_list_item(const char* list, const char* item)
206 const char* p = list;
207 int itemlen = strlen(item);
209 if (list == NULL)
210 return 0;
212 while (*p) {
213 const char* q;
215 /* skip spaces */
216 while (*p == ' ' || *p == '\t')
217 p++;
219 /* find end of current list item */
220 q = p;
221 while (*q && *q != ' ' && *q != '\t')
222 q++;
224 if (itemlen == q-p && !memcmp(p, item, itemlen))
225 return 1;
227 /* skip to next item */
228 p = q;
230 return 0;
234 static void
235 android_cpuInit(void)
237 char cpuinfo[4096];
238 int cpuinfo_len;
240 g_cpuFamily = DEFAULT_CPU_FAMILY;
241 g_cpuFeatures = 0;
242 g_cpuCount = 1;
244 cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
245 D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len,
246 cpuinfo_len >= 0 ? cpuinfo_len : 0, cpuinfo);
248 if (cpuinfo_len < 0) /* should not happen */ {
249 return;
252 /* Count the CPU cores, the value may be 0 for single-core CPUs */
253 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "processor");
254 if (g_cpuCount == 0) {
255 g_cpuCount = count_cpuinfo_field(cpuinfo, cpuinfo_len, "Processor");
256 if (g_cpuCount == 0) {
257 g_cpuCount = 1;
261 D("found cpuCount = %d\n", g_cpuCount);
263 #ifdef __ARM_ARCH__
265 char* features = NULL;
266 char* architecture = NULL;
268 /* Extract architecture from the "CPU Architecture" field.
269 * The list is well-known, unlike the the output of
270 * the 'Processor' field which can vary greatly.
272 * See the definition of the 'proc_arch' array in
273 * $KERNEL/arch/arm/kernel/setup.c and the 'c_show' function in
274 * same file.
276 char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
278 if (cpuArch != NULL) {
279 char* end;
280 long archNumber;
281 int hasARMv7 = 0;
283 D("found cpuArch = '%s'\n", cpuArch);
285 /* read the initial decimal number, ignore the rest */
286 archNumber = strtol(cpuArch, &end, 10);
288 /* Here we assume that ARMv8 will be upwards compatible with v7
289 * in the future. Unfortunately, there is no 'Features' field to
290 * indicate that Thumb-2 is supported.
292 if (end > cpuArch && archNumber >= 7) {
293 hasARMv7 = 1;
296 /* Unfortunately, it seems that certain ARMv6-based CPUs
297 * report an incorrect architecture number of 7!
299 * See http://code.google.com/p/android/issues/detail?id=10812
301 * We try to correct this by looking at the 'elf_format'
302 * field reported by the 'Processor' field, which is of the
303 * form of "(v7l)" for an ARMv7-based CPU, and "(v6l)" for
304 * an ARMv6-one.
306 if (hasARMv7) {
307 char* cpuProc = extract_cpuinfo_field(cpuinfo, cpuinfo_len,
308 "Processor");
309 if (cpuProc != NULL) {
310 D("found cpuProc = '%s'\n", cpuProc);
311 if (has_list_item(cpuProc, "(v6l)")) {
312 D("CPU processor and architecture mismatch!!\n");
313 hasARMv7 = 0;
315 free(cpuProc);
319 if (hasARMv7) {
320 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
323 /* The LDREX / STREX instructions are available from ARMv6 */
324 if (archNumber >= 6) {
325 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_LDREX_STREX;
328 free(cpuArch);
331 /* Extract the list of CPU features from 'Features' field */
332 char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
334 if (cpuFeatures != NULL) {
336 D("found cpuFeatures = '%s'\n", cpuFeatures);
338 if (has_list_item(cpuFeatures, "vfpv3"))
339 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
341 else if (has_list_item(cpuFeatures, "vfpv3d16"))
342 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
344 if (has_list_item(cpuFeatures, "neon")) {
345 /* Note: Certain kernels only report neon but not vfpv3
346 * in their features list. However, ARM mandates
347 * that if Neon is implemented, so must be VFPv3
348 * so always set the flag.
350 g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON |
351 ANDROID_CPU_ARM_FEATURE_VFPv3;
353 free(cpuFeatures);
356 #endif /* __ARM_ARCH__ */
358 #ifdef __i386__
359 g_cpuFamily = ANDROID_CPU_FAMILY_X86;
360 #endif
364 AndroidCpuFamily
365 android_getCpuFamily(void)
367 pthread_once(&g_once, android_cpuInit);
368 return g_cpuFamily;
372 uint64_t
373 android_getCpuFeatures(void)
375 pthread_once(&g_once, android_cpuInit);
376 return g_cpuFeatures;
381 android_getCpuCount(void)
383 pthread_once(&g_once, android_cpuInit);
384 return g_cpuCount;