1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "libavutil/cpu.h"
20 #include "libavutil/cpu_internal.h"
21 #include "config.h"
22 
23 #define CORE_FLAG(f) \
24     (AV_CPU_FLAG_ ## f * (HAVE_ ## f ## _EXTERNAL || HAVE_ ## f ## _INLINE))
25 
26 #define CORE_CPU_FLAGS                          \
27     (CORE_FLAG(ARMV5TE) |                       \
28      CORE_FLAG(ARMV6)   |                       \
29      CORE_FLAG(ARMV6T2) |                       \
30      CORE_FLAG(VFP)     |                       \
31      CORE_FLAG(VFPV3)   |                       \
32      CORE_FLAG(NEON))
33 
34 #if defined __linux__ || defined __ANDROID__
35 
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include "libavutil/avstring.h"
40 
41 #if HAVE_GETAUXVAL
42 #include <sys/auxv.h>
43 #endif
44 
45 #define AT_HWCAP        16
46 
47 /* Relevant HWCAP values from kernel headers */
48 #define HWCAP_VFP       (1 << 6)
49 #define HWCAP_EDSP      (1 << 7)
50 #define HWCAP_THUMBEE   (1 << 11)
51 #define HWCAP_NEON      (1 << 12)
52 #define HWCAP_VFPv3     (1 << 13)
53 #define HWCAP_TLS       (1 << 15)
54 
get_auxval(uint32_t *hwcap)55 static int get_auxval(uint32_t *hwcap)
56 {
57 #if HAVE_GETAUXVAL
58     unsigned long ret = getauxval(AT_HWCAP);
59     if (ret == 0)
60         return -1;
61     *hwcap = ret;
62     return 0;
63 #else
64     return -1;
65 #endif
66 }
67 
get_hwcap(uint32_t *hwcap)68 static int get_hwcap(uint32_t *hwcap)
69 {
70     struct { uint32_t a_type; uint32_t a_val; } auxv;
71     FILE *f = fopen("/proc/self/auxv", "r");
72     int err = -1;
73 
74     if (!f)
75         return -1;
76 
77     while (fread(&auxv, sizeof(auxv), 1, f) > 0) {
78         if (auxv.a_type == AT_HWCAP) {
79             *hwcap = auxv.a_val;
80             err = 0;
81             break;
82         }
83     }
84 
85     fclose(f);
86     return err;
87 }
88 
get_cpuinfo(uint32_t *hwcap)89 static int get_cpuinfo(uint32_t *hwcap)
90 {
91     FILE *f = fopen("/proc/cpuinfo", "r");
92     char buf[200];
93 
94     if (!f)
95         return -1;
96 
97     *hwcap = 0;
98     while (fgets(buf, sizeof(buf), f)) {
99         if (av_strstart(buf, "Features", NULL)) {
100             if (strstr(buf, " edsp "))
101                 *hwcap |= HWCAP_EDSP;
102             if (strstr(buf, " tls "))
103                 *hwcap |= HWCAP_TLS;
104             if (strstr(buf, " thumbee "))
105                 *hwcap |= HWCAP_THUMBEE;
106             if (strstr(buf, " vfp "))
107                 *hwcap |= HWCAP_VFP;
108             if (strstr(buf, " vfpv3 "))
109                 *hwcap |= HWCAP_VFPv3;
110             if (strstr(buf, " neon ") || strstr(buf, " asimd "))
111                 *hwcap |= HWCAP_NEON;
112             if (strstr(buf, " fp ")) // Listed on 64 bit ARMv8 kernels
113                 *hwcap |= HWCAP_VFP | HWCAP_VFPv3;
114             break;
115         }
116     }
117     fclose(f);
118     return 0;
119 }
120 
ff_get_cpu_flags_arm(void)121 int ff_get_cpu_flags_arm(void)
122 {
123     int flags = CORE_CPU_FLAGS;
124     uint32_t hwcap;
125 
126     if (get_auxval(&hwcap) < 0)
127         if (get_hwcap(&hwcap) < 0)
128             if (get_cpuinfo(&hwcap) < 0)
129                 return flags;
130 
131 #define check_cap(cap, flag) do {               \
132         if (hwcap & HWCAP_ ## cap)              \
133             flags |= AV_CPU_FLAG_ ## flag;      \
134     } while (0)
135 
136     /* No flags explicitly indicate v6 or v6T2 so check others which
137        imply support. */
138     check_cap(EDSP,    ARMV5TE);
139     check_cap(TLS,     ARMV6);
140     check_cap(THUMBEE, ARMV6T2);
141     check_cap(VFP,     VFP);
142     check_cap(VFPv3,   VFPV3);
143     check_cap(NEON,    NEON);
144 
145     /* The v6 checks above are not reliable so let higher flags
146        trickle down. */
147     if (flags & (AV_CPU_FLAG_VFPV3 | AV_CPU_FLAG_NEON))
148         flags |= AV_CPU_FLAG_ARMV6T2;
149     else if (flags & (AV_CPU_FLAG_ARMV6T2 | AV_CPU_FLAG_ARMV6))
150     /* Some functions use the 'setend' instruction which is deprecated on ARMv8
151      * and serializing on some ARMv7 cores. This ensures such functions
152      * are only enabled on ARMv6. */
153         flags |= AV_CPU_FLAG_SETEND;
154 
155     if (flags & AV_CPU_FLAG_ARMV6T2)
156         flags |= AV_CPU_FLAG_ARMV6;
157 
158     /* set the virtual VFPv2 vector mode flag */
159     if ((flags & AV_CPU_FLAG_VFP) && !(flags & (AV_CPU_FLAG_VFPV3 | AV_CPU_FLAG_NEON)))
160         flags |= AV_CPU_FLAG_VFP_VM;
161 
162     return flags;
163 }
164 
165 #else
166 
ff_get_cpu_flags_arm(void)167 int ff_get_cpu_flags_arm(void)
168 {
169     return AV_CPU_FLAG_ARMV5TE * HAVE_ARMV5TE |
170            AV_CPU_FLAG_ARMV6   * HAVE_ARMV6   |
171            AV_CPU_FLAG_ARMV6T2 * HAVE_ARMV6T2 |
172            AV_CPU_FLAG_VFP     * HAVE_VFP     |
173            AV_CPU_FLAG_VFPV3   * HAVE_VFPV3   |
174            AV_CPU_FLAG_NEON    * HAVE_NEON    |
175            AV_CPU_FLAG_SETEND  * !(HAVE_NEON | HAVE_VFPV3);
176 }
177 
178 #endif
179 
ff_get_cpu_max_align_arm(void)180 size_t ff_get_cpu_max_align_arm(void)
181 {
182     int flags = av_get_cpu_flags();
183 
184     if (flags & AV_CPU_FLAG_NEON)
185         return 16;
186 
187     return 8;
188 }
189