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 55static 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 68static 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 89static 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 121int 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 167int 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 180size_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