• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 <filesystem>
18 #include <optional>
19 
20 #include <android-base/properties.h>
21 #include <android-base/strings.h>
22 #include <gmock/gmock.h>
23 #include <gtest/gtest.h>
24 #include <kver/kernel_release.h>
25 #include <vintf/VintfObject.h>
26 #include <vintf/parse_string.h>
27 
28 #include "ramdisk_utils.h"
29 
30 using android::base::GetBoolProperty;
31 using android::base::GetProperty;
32 using android::kver::KernelRelease;
33 using android::vintf::Level;
34 using android::vintf::RuntimeInfo;
35 using android::vintf::Version;
36 using android::vintf::VintfObject;
37 using testing::IsSupersetOf;
38 
39 // Returns true iff the device has the specified feature.
deviceSupportsFeature(const char * feature)40 static bool deviceSupportsFeature(const char *feature) {
41   bool device_supports_feature = false;
42   FILE *p = popen("pm list features", "re");
43   if (p) {
44     char *line = NULL;
45     size_t len = 0;
46     while (getline(&line, &len, p) > 0) {
47       if (strstr(line, feature)) {
48         device_supports_feature = true;
49         break;
50       }
51     }
52     if (line) {
53       free(line);
54       line = NULL;
55     }
56     pclose(p);
57   }
58   return device_supports_feature;
59 }
60 
isTV()61 static bool isTV() {
62   return deviceSupportsFeature("android.software.leanback");
63 }
64 
get_config(const std::map<std::string,std::string> & configs,const std::string & key)65 std::optional<std::string> get_config(
66     const std::map<std::string, std::string>& configs, const std::string& key) {
67   auto it = configs.find(key);
68   if (it == configs.end()) {
69     return std::nullopt;
70   }
71   return it->second;
72 }
73 
74 class GenericBootImageTest : public testing::Test {
75  public:
SetUp()76   void SetUp() override {
77     auto vintf = VintfObject::GetInstance();
78     ASSERT_NE(nullptr, vintf);
79     runtime_info = vintf->getRuntimeInfo(RuntimeInfo::FetchFlag::CPU_VERSION |
80                                          RuntimeInfo::FetchFlag::CONFIG_GZ);
81     ASSERT_NE(nullptr, runtime_info);
82 
83     const auto& configs = runtime_info->kernelConfigs();
84     if (get_config(configs, "CONFIG_ARM") == "y") {
85       GTEST_SKIP() << "Skipping on 32-bit ARM devices";
86     } else if (get_config(configs, "CONFIG_X86") == "y" ||
87                get_config(configs, "CONFIG_X86_64") == "y") {
88       GTEST_SKIP() << "Skipping on X86 & X86_64 devices";
89     }
90     // Technically, the test should also be skipped on CONFIG_X86 and
91     // CONFIG_X86_64, and only run on CONFIG_ARM64,
92     // but we want to keep this test passing on virtual
93     // device targets, and we don't have any requests to skip this test
94     // on x86 / x86_64 as of 2022-06-07.
95 
96     int firstApiLevel = std::stoi(android::base::GetProperty("ro.product.first_api_level", "0"));
97     if (isTV() && firstApiLevel <= __ANDROID_API_T__) {
98       GTEST_SKIP() << "Skipping on TV devices";
99     }
100   }
101   std::shared_ptr<const RuntimeInfo> runtime_info;
102 };
103 
TEST_F(GenericBootImageTest,KernelReleaseFormat)104 TEST_F(GenericBootImageTest, KernelReleaseFormat) {
105   // On "GKI 2.0" with 5.10+ kernels, VTS runs once with the device kernel,
106   // so this test is meaningful.
107   if (runtime_info->kernelVersion().dropMinor() < Version{5, 10}) {
108     GTEST_SKIP() << "Exempt generic kernel image (GKI) test on kernel "
109                  << runtime_info->kernelVersion()
110                  << ". Only required on 5.10+.";
111   }
112 
113   const std::string& release = runtime_info->osRelease();
114   ASSERT_TRUE(
115       KernelRelease::Parse(release, true /* allow_suffix */).has_value())
116       << "Kernel release '" << release
117       << "' does not have generic kernel image (GKI) release format. It must "
118          "match this regex:\n"
119       << R"(^(?P<w>\d+)[.](?P<x>\d+)[.](?P<y>\d+)-(?P<z>android\d+)-(?P<k>\d+).*$)"
120       << "\nExample: 5.4.42-android12-0-something";
121 }
122 
GetRequirementBySdkLevel(uint32_t target_sdk_level)123 std::set<std::string> GetRequirementBySdkLevel(uint32_t target_sdk_level) {
124   // Files which must be present in generic ramdisk. This list acts as a lower
125   // bound for device's ramdisk.
126   static const std::map<uint32_t, std::set<std::string>> required_by_level = {
127       {0, {"init", "system/etc/ramdisk/build.prop"}},  // or some other number?
128       {
129           __ANDROID_API_T__,
130           {"system/bin/snapuserd", "system/etc/init/snapuserd.rc"},
131       }};
132   std::set<std::string> res;
133   for (const auto& [level, requirements] : required_by_level) {
134     if (level > target_sdk_level) {
135       break;
136     }
137     res.insert(requirements.begin(), requirements.end());
138   }
139   return res;
140 }
141 
GetAllowListBySdkLevel(uint32_t target_sdk_level)142 std::set<std::string> GetAllowListBySdkLevel(uint32_t target_sdk_level) {
143   // Files that are allowed in generic ramdisk(but not necessarily required)
144   // This list acts as an upper bound for what the device's ramdisk can possibly
145   // contain.
146   static const std::map<uint32_t, std::set<std::string>> allow_by_level = {
147       {__ANDROID_API_T__, {"system/bin/snapuserd_ramdisk"}},
148       {__ANDROID_API_U__, {"dev/console", "dev/null", "dev/urandom"}},
149       {
150           __ANDROID_API_V__,
151           {
152               "system/bin/toolbox_ramdisk",
153               "system/bin/modprobe",
154               "system/bin/start",
155               "system/bin/stop",
156               "system/bin/setprop",
157               "system/bin/getprop",
158               "system/bin/getevent",
159           },
160       },
161   };
162   auto res = GetRequirementBySdkLevel(target_sdk_level);
163   for (const auto& [level, requirements] : allow_by_level) {
164     if (level > target_sdk_level) {
165       break;
166     }
167     res.insert(requirements.begin(), requirements.end());
168   }
169   return res;
170 }
171 
TEST_F(GenericBootImageTest,GenericRamdisk)172 TEST_F(GenericBootImageTest, GenericRamdisk) {
173   // On "GKI 2.0" with 5.10+ kernels, VTS runs once with the device kernel,
174   // so this test is meaningful.
175   if (runtime_info->kernelVersion().dropMinor() < Version{5, 10}) {
176     GTEST_SKIP() << "Exempt generic ramdisk test on kernel "
177                  << runtime_info->kernelVersion()
178                  << ". Only required on 5.10+.";
179     return;
180   }
181 
182   using std::filesystem::recursive_directory_iterator;
183 
184   std::string slot_suffix = GetProperty("ro.boot.slot_suffix", "");
185   // Launching devices with T+ using android13+ kernels have the ramdisk in
186   // init_boot instead of boot
187   std::string error_msg;
188   const auto kernel_level =
189       VintfObject::GetInstance()->getKernelLevel(&error_msg);
190   ASSERT_NE(Level::UNSPECIFIED, kernel_level) << error_msg;
191   std::string boot_path;
192   if (kernel_level >= Level::T) {
193     int first_api_level = android::base::GetIntProperty(
194         "ro.board.first_api_level",
195         android::base::GetIntProperty("ro.vendor.api_level", 1000000));
196     if (first_api_level >= __ANDROID_API_T__) {
197       boot_path = "/dev/block/by-name/init_boot" + slot_suffix;
198     } else {
199       // This is the case of a device launched before Android 13 that is
200       // upgrading its kernel to android13+. These devices can't add an
201       // init_boot partition and need to include the equivalent ramdisk
202       // functionality somewhere outside of boot.img (most likely in the
203       // vendor_boot image). Since we don't know where to look, or which files
204       // will be present, we can skip the rest of this test case.
205       GTEST_SKIP() << "Exempt generic ramdisk test on upgrading device that "
206                    << "launched before Android 13 and is now using an Android "
207                    << "13+ kernel.";
208       return;
209     }
210   } else {
211     boot_path = "/dev/block/by-name/boot" + slot_suffix;
212   }
213   if (0 != access(boot_path.c_str(), R_OK)) {
214     int saved_errno = errno;
215     FAIL() << "Can't access " << boot_path << ": " << strerror(saved_errno);
216     return;
217   }
218 
219   const auto extracted_ramdisk = android::ExtractRamdiskToDirectory(boot_path);
220   ASSERT_TRUE(extracted_ramdisk.ok())
221       << "Failed to extract ramdisk: " << extracted_ramdisk.error();
222 
223   std::set<std::string> actual_files;
224   const std::filesystem::path extracted_ramdisk_path((*extracted_ramdisk)->path);
225   for (auto& p : recursive_directory_iterator(extracted_ramdisk_path)) {
226     if (p.is_directory()) continue;
227     auto rel_path = p.path().lexically_relative(extracted_ramdisk_path);
228     actual_files.insert(rel_path.string());
229   }
230 
231   const auto sdk_level =
232       android::base::GetIntProperty("ro.bootimage.build.version.sdk", 0);
233   const std::set<std::string> generic_ramdisk_required_list =
234       GetRequirementBySdkLevel(sdk_level);
235   std::set<std::string> generic_ramdisk_allow_list =
236       GetAllowListBySdkLevel(sdk_level);
237 
238   const bool is_debuggable = GetBoolProperty("ro.debuggable", false);
239   if (is_debuggable) {
240     const std::set<std::string> debuggable_allowlist{
241         "adb_debug.prop",
242         "force_debuggable",
243         "userdebug_plat_sepolicy.cil",
244     };
245     generic_ramdisk_allow_list.insert(debuggable_allowlist.begin(),
246                                       debuggable_allowlist.end());
247   }
248   EXPECT_THAT(actual_files, IsSupersetOf(generic_ramdisk_required_list))
249       << "Missing files required by " << (is_debuggable ? "debuggable " : "")
250       << "generic ramdisk";
251   EXPECT_THAT(generic_ramdisk_allow_list, IsSupersetOf(actual_files))
252       << "Contains files disallowed by " << (is_debuggable ? "debuggable " : "")
253       << "generic ramdisk";
254 }
255