File size: 3,709 Bytes
8b7c501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <string.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dlfcn.h>
#include <elf.h>

#if CPUINFO_MOCK
	#include <cpuinfo-mock.h>
#endif
#include <cpuinfo.h>
#include <arm/linux/api.h>
#include <cpuinfo/log.h>

#if CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_ARM && !defined(__ANDROID__)
	#include <sys/auxv.h>
#else
	#define AT_HWCAP 16
	#define AT_HWCAP2 26
#endif


#if CPUINFO_MOCK
	static uint32_t mock_hwcap = 0;
	void cpuinfo_set_hwcap(uint32_t hwcap) {
		mock_hwcap = hwcap;
	}

	static uint32_t mock_hwcap2 = 0;
	void cpuinfo_set_hwcap2(uint32_t hwcap2) {
		mock_hwcap2 = hwcap2;
	}
#endif


#if CPUINFO_ARCH_ARM
	typedef unsigned long (*getauxval_function_t)(unsigned long);

	bool cpuinfo_arm_linux_hwcap_from_getauxval(
		uint32_t hwcap[restrict static 1],
		uint32_t hwcap2[restrict static 1])
	{
		#if CPUINFO_MOCK
			*hwcap  = mock_hwcap;
			*hwcap2 = mock_hwcap2;
			return true;
		#elif defined(__ANDROID__)
			/* Android: dynamically check if getauxval is supported */
			void* libc = NULL;
			getauxval_function_t getauxval = NULL;

			dlerror();
			libc = dlopen("libc.so", RTLD_LAZY);
			if (libc == NULL) {
				cpuinfo_log_warning("failed to load libc.so: %s", dlerror());
				goto cleanup;
			}

			getauxval = (getauxval_function_t) dlsym(libc, "getauxval");
			if (getauxval == NULL) {
				cpuinfo_log_info("failed to locate getauxval in libc.so: %s", dlerror());
				goto cleanup;
			}

			*hwcap  = getauxval(AT_HWCAP);
			*hwcap2 = getauxval(AT_HWCAP2);

		cleanup:
			if (libc != NULL) {
				dlclose(libc);
				libc = NULL;
			}
			return getauxval != NULL;
		#else
			/* GNU/Linux: getauxval is always supported */
			*hwcap  = getauxval(AT_HWCAP);
			*hwcap2 = getauxval(AT_HWCAP2);
			return true;
		#endif
	}

	#ifdef __ANDROID__
		bool cpuinfo_arm_linux_hwcap_from_procfs(
			uint32_t hwcap[restrict static 1],
			uint32_t hwcap2[restrict static 1])
		{
			#if CPUINFO_MOCK
				*hwcap  = mock_hwcap;
				*hwcap2 = mock_hwcap2;
				return true;
			#else
				uint32_t hwcaps[2] = { 0, 0 };
				bool result = false;
				int file = -1;

				file = open("/proc/self/auxv", O_RDONLY);
				if (file == -1) {
					cpuinfo_log_warning("failed to open /proc/self/auxv: %s", strerror(errno));
					goto cleanup;
				}

				ssize_t bytes_read;
				do {
					Elf32_auxv_t elf_auxv;
					bytes_read = read(file, &elf_auxv, sizeof(Elf32_auxv_t));
					if (bytes_read < 0) {
						cpuinfo_log_warning("failed to read /proc/self/auxv: %s", strerror(errno));
						goto cleanup;
					} else if (bytes_read > 0) {
						if (bytes_read == sizeof(elf_auxv)) {
							switch (elf_auxv.a_type) {
								case AT_HWCAP:
									hwcaps[0] = (uint32_t) elf_auxv.a_un.a_val;
									break;
								case AT_HWCAP2:
									hwcaps[1] = (uint32_t) elf_auxv.a_un.a_val;
									break;
							}
						} else {
							cpuinfo_log_warning(
								"failed to read %zu bytes from /proc/self/auxv: %zu bytes available",
								sizeof(elf_auxv), (size_t) bytes_read);
							goto cleanup;
						}
					}
				} while (bytes_read == sizeof(Elf32_auxv_t));

				/* Success, commit results */
				*hwcap  = hwcaps[0];
				*hwcap2 = hwcaps[1];
				result = true;

			cleanup:
				if (file != -1) {
					close(file);
					file = -1;
				}
				return result;
			#endif
		}
	#endif /* __ANDROID__ */
#elif CPUINFO_ARCH_ARM64
	void cpuinfo_arm_linux_hwcap_from_getauxval(
		uint32_t hwcap[restrict static 1],
		uint32_t hwcap2[restrict static 1])
	{
		#if CPUINFO_MOCK
			*hwcap  = mock_hwcap;
			*hwcap2 = mock_hwcap2;
		#else
			*hwcap  = (uint32_t) getauxval(AT_HWCAP);
			*hwcap2 = (uint32_t) getauxval(AT_HWCAP2);
			return ;
		#endif
	}
#endif