1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "gtest/gtest.h"
18
19 #include <asm/hwcap.h>
20 #include <cpu-features.h>
21 #include <sys/auxv.h>
22 #include <sys/system_properties.h>
23
24 #include <fstream>
25 #include <string>
26
27 namespace {
28
29 // Test CPU features interfaces from
30 // https://developer.android.com/ndk/guides/cpu-features
31 //
32 // Required features for arm64-v8a from
33 // https://developer.android.com/ndk/guides/abis
34 // is Armv8.0 only, which assumes the following features:
35 // fp asimd aes pmull sha1 sha2
36 //
37 // For ndk-translation we check that we get all the values as expected.
38
39 class ProcCpuinfoFeatures {
40 public:
ProcCpuinfoFeatures(const char * cpuinfo_path)41 ProcCpuinfoFeatures(const char* cpuinfo_path) {
42 std::ifstream cpuinfo(cpuinfo_path);
43 std::string line;
44 while (std::getline(cpuinfo, line)) {
45 // Warning: line caption for features is architecture dependent!
46 if (line.find("Features") != std::string::npos) {
47 features_ = line;
48 features_ += " "; // add feature delimiter at the end.
49 }
50 }
51 }
52
Empty() const53 bool Empty() const { return features_.empty(); }
54
Get(const char * name) const55 bool Get(const char* name) const {
56 // Search for name surrounded by delimiters.
57 std::string value = " ";
58 value += name;
59 value += " ";
60 return features_.find(name) != std::string::npos;
61 }
62
63 private:
64 std::string features_;
65 };
66
67 class BitFeatures {
68 public:
BitFeatures(uint64_t features)69 explicit BitFeatures(uint64_t features) : features_(features) {}
70
Empty() const71 bool Empty() const { return features_ == 0; }
72
Get(uint64_t bits) const73 bool Get(uint64_t bits) const { return (features_ & bits) != 0; }
74
75 private:
76 uint64_t features_;
77 };
78
IsNdkTranslation()79 bool IsNdkTranslation() {
80 char value[PROP_VALUE_MAX];
81 __system_property_get("ro.dalvik.vm.native.bridge", value);
82 return std::string(value) == "libndk_translation.so";
83 }
84
TEST(Arm64CpuFeatures,proc_cpuinfo)85 TEST(Arm64CpuFeatures, proc_cpuinfo) {
86 ProcCpuinfoFeatures cpuinfo("/proc/cpuinfo");
87 // Attention: ART mounts guest cpuinfo in case of native bridge. No one does
88 // that in case of standalone executable, so by default we observe the host one.
89 if (cpuinfo.Empty()) {
90 GTEST_LOG_(INFO) << "/proc/cpuinfo features are empty (arm64 cpuinfo isn't mounted?),"
91 << " trying /etc/cpuinfo.arm64.txt\n";
92
93 cpuinfo = ProcCpuinfoFeatures("/etc/cpuinfo.arm64.txt");
94
95 if (cpuinfo.Empty()) {
96 GTEST_SKIP() << "No arm64 cpuinfo found";
97 }
98 }
99 // fp asimd aes pmull sha1 sha2
100 EXPECT_TRUE(cpuinfo.Get("fp"));
101 EXPECT_TRUE(cpuinfo.Get("asimd"));
102 EXPECT_TRUE(cpuinfo.Get("aes"));
103 EXPECT_TRUE(cpuinfo.Get("pmull"));
104
105 if (IsNdkTranslation()) {
106 EXPECT_TRUE(cpuinfo.Get("crc32"));
107 } else {
108 EXPECT_TRUE(cpuinfo.Get("sha1"));
109 EXPECT_TRUE(cpuinfo.Get("sha2"));
110 }
111 }
112
TEST(Arm64CpuFeatures,getauxval_AT_HWCAP)113 TEST(Arm64CpuFeatures, getauxval_AT_HWCAP) {
114 BitFeatures hwcap(getauxval(AT_HWCAP));
115
116 EXPECT_TRUE(hwcap.Get(HWCAP_FP));
117 EXPECT_TRUE(hwcap.Get(HWCAP_ASIMD));
118 EXPECT_TRUE(hwcap.Get(HWCAP_AES));
119 EXPECT_TRUE(hwcap.Get(HWCAP_PMULL));
120
121 if (IsNdkTranslation()) {
122 EXPECT_TRUE(hwcap.Get(HWCAP_CRC32));
123 } else {
124 EXPECT_TRUE(hwcap.Get(HWCAP_SHA1));
125 EXPECT_TRUE(hwcap.Get(HWCAP_SHA2));
126 }
127 }
128
TEST(Arm64CpuFeatures,getauxval_AT_HWCAP2)129 TEST(Arm64CpuFeatures, getauxval_AT_HWCAP2) {
130 if (!IsNdkTranslation()) {
131 return;
132 }
133 BitFeatures hwcap2(getauxval(AT_HWCAP2));
134 ASSERT_TRUE(hwcap2.Empty());
135 }
136
TEST(Arm64CpuFeatures,android_getCpuFeatures)137 TEST(Arm64CpuFeatures, android_getCpuFeatures) {
138 AndroidCpuFamily cpu_family = android_getCpuFamily();
139 ASSERT_EQ(cpu_family, ANDROID_CPU_FAMILY_ARM64);
140
141 BitFeatures android_cpu_features(android_getCpuFeatures());
142
143 EXPECT_TRUE(android_cpu_features.Get(ANDROID_CPU_ARM64_FEATURE_FP));
144 EXPECT_TRUE(android_cpu_features.Get(ANDROID_CPU_ARM64_FEATURE_ASIMD));
145 EXPECT_TRUE(android_cpu_features.Get(ANDROID_CPU_ARM64_FEATURE_AES));
146 EXPECT_TRUE(android_cpu_features.Get(ANDROID_CPU_ARM64_FEATURE_PMULL));
147
148 if (IsNdkTranslation()) {
149 EXPECT_TRUE(android_cpu_features.Get(ANDROID_CPU_ARM64_FEATURE_CRC32));
150 } else {
151 EXPECT_TRUE(android_cpu_features.Get(ANDROID_CPU_ARM64_FEATURE_SHA1));
152 EXPECT_TRUE(android_cpu_features.Get(ANDROID_CPU_ARM64_FEATURE_SHA2));
153 }
154 }
155
156 } // namespace
157