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