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 }
87 // Technically, the test should also be skipped on CONFIG_X86 and
88 // CONFIG_X86_64, and only run on CONFIG_ARM64,
89 // but we want to keep this test passing on virtual
90 // device targets, and we don't have any requests to skip this test
91 // on x86 / x86_64 as of 2022-06-07.
92
93 int firstApiLevel = std::stoi(android::base::GetProperty("ro.product.first_api_level", "0"));
94 if (isTV() && firstApiLevel <= __ANDROID_API_T__) {
95 GTEST_SKIP() << "Skipping on TV devices";
96 }
97 }
98 std::shared_ptr<const RuntimeInfo> runtime_info;
99 };
100
TEST_F(GenericBootImageTest,KernelReleaseFormat)101 TEST_F(GenericBootImageTest, KernelReleaseFormat) {
102 // On "GKI 2.0" with 5.10+ kernels, VTS runs once with the device kernel,
103 // so this test is meaningful.
104 if (runtime_info->kernelVersion().dropMinor() < Version{5, 10}) {
105 GTEST_SKIP() << "Exempt generic kernel image (GKI) test on kernel "
106 << runtime_info->kernelVersion()
107 << ". Only required on 5.10+.";
108 }
109
110 const std::string& release = runtime_info->osRelease();
111 ASSERT_TRUE(
112 KernelRelease::Parse(release, true /* allow_suffix */).has_value())
113 << "Kernel release '" << release
114 << "' does not have generic kernel image (GKI) release format. It must "
115 "match this regex:\n"
116 << R"(^(?P<w>\d+)[.](?P<x>\d+)[.](?P<y>\d+)-(?P<z>android\d+)-(?P<k>\d+).*$)"
117 << "\nExample: 5.4.42-android12-0-something";
118 }
119
GetRequirementBySdkLevel(uint32_t target_sdk_level)120 std::set<std::string> GetRequirementBySdkLevel(uint32_t target_sdk_level) {
121 // Files which must be present in generic ramdisk. This list acts as a lower
122 // bound for device's ramdisk.
123 static const std::map<uint32_t, std::set<std::string>> required_by_level = {
124 {0, {"init", "system/etc/ramdisk/build.prop"}}, // or some other number?
125 {
126 __ANDROID_API_T__,
127 {"system/bin/snapuserd", "system/etc/init/snapuserd.rc"},
128 }};
129 std::set<std::string> res;
130 for (const auto& [level, requirements] : required_by_level) {
131 if (level > target_sdk_level) {
132 break;
133 }
134 res.insert(requirements.begin(), requirements.end());
135 }
136 return res;
137 }
138
GetAllowListBySdkLevel(uint32_t target_sdk_level)139 std::set<std::string> GetAllowListBySdkLevel(uint32_t target_sdk_level) {
140 // Files that are allowed in generic ramdisk(but not necessarily required)
141 // This list acts as an upper bound for what the device's ramdisk can possibly
142 // contain.
143 static const std::map<uint32_t, std::set<std::string>> allow_by_level = {{
144 __ANDROID_API_T__,
145 {"system/bin/snapuserd_ramdisk"},
146 }};
147 auto res = GetRequirementBySdkLevel(target_sdk_level);
148 for (const auto& [level, requirements] : allow_by_level) {
149 if (level > target_sdk_level) {
150 break;
151 }
152 res.insert(requirements.begin(), requirements.end());
153 }
154 return res;
155 }
156
TEST_F(GenericBootImageTest,GenericRamdisk)157 TEST_F(GenericBootImageTest, GenericRamdisk) {
158 // On "GKI 2.0" with 5.10+ kernels, VTS runs once with the device kernel,
159 // so this test is meaningful.
160 if (runtime_info->kernelVersion().dropMinor() < Version{5, 10}) {
161 GTEST_SKIP() << "Exempt generic ramdisk test on kernel "
162 << runtime_info->kernelVersion()
163 << ". Only required on 5.10+.";
164 return;
165 }
166
167 using std::filesystem::recursive_directory_iterator;
168
169 std::string slot_suffix = GetProperty("ro.boot.slot_suffix", "");
170 // Launching devices with T+ using android13+ kernels have the ramdisk in
171 // init_boot instead of boot
172 std::string error_msg;
173 const auto kernel_level =
174 VintfObject::GetInstance()->getKernelLevel(&error_msg);
175 ASSERT_NE(Level::UNSPECIFIED, kernel_level) << error_msg;
176 std::string boot_path;
177 if (kernel_level >= Level::T) {
178 if (std::stoi(android::base::GetProperty("ro.vendor.api_level", "0")) >=
179 __ANDROID_API_T__) {
180 boot_path = "/dev/block/by-name/init_boot" + slot_suffix;
181 } else {
182 // This is the case of a device launched before Android 13 that is
183 // upgrading its kernel to android13+. These devices can't add an
184 // init_boot partition and need to include the equivalent ramdisk
185 // functionality somewhere outside of boot.img (most likely in the
186 // vendor_boot image). Since we don't know where to look, or which files
187 // will be present, we can skip the rest of this test case.
188 GTEST_SKIP() << "Exempt generic ramdisk test on upgrading device that "
189 << "launched before Android 13 and is now using an Android "
190 << "13+ kernel.";
191 return;
192 }
193 } else {
194 boot_path = "/dev/block/by-name/boot" + slot_suffix;
195 }
196 if (0 != access(boot_path.c_str(), R_OK)) {
197 int saved_errno = errno;
198 FAIL() << "Can't access " << boot_path << ": " << strerror(saved_errno);
199 return;
200 }
201
202 const auto extracted_ramdisk = android::ExtractRamdiskToDirectory(boot_path);
203 ASSERT_TRUE(extracted_ramdisk.ok())
204 << "Failed to extract ramdisk: " << extracted_ramdisk.error();
205
206 std::set<std::string> actual_files;
207 const std::filesystem::path extracted_ramdisk_path((*extracted_ramdisk)->path);
208 for (auto& p : recursive_directory_iterator(extracted_ramdisk_path)) {
209 if (p.is_directory()) continue;
210 EXPECT_TRUE(p.is_regular_file())
211 << "Unexpected non-regular file " << p.path();
212 auto rel_path = p.path().lexically_relative(extracted_ramdisk_path);
213 actual_files.insert(rel_path.string());
214 }
215
216 const auto sdk_level =
217 android::base::GetIntProperty("ro.bootimage.build.version.sdk", 0);
218 const std::set<std::string> generic_ramdisk_required_list =
219 GetRequirementBySdkLevel(sdk_level);
220 std::set<std::string> generic_ramdisk_allow_list =
221 GetAllowListBySdkLevel(sdk_level);
222
223 const bool is_debuggable = GetBoolProperty("ro.debuggable", false);
224 if (is_debuggable) {
225 const std::set<std::string> debuggable_allowlist{
226 "adb_debug.prop",
227 "force_debuggable",
228 "userdebug_plat_sepolicy.cil",
229 };
230 generic_ramdisk_allow_list.insert(debuggable_allowlist.begin(),
231 debuggable_allowlist.end());
232 }
233 EXPECT_THAT(actual_files, IsSupersetOf(generic_ramdisk_required_list))
234 << "Missing files required by " << (is_debuggable ? "debuggable " : "")
235 << "generic ramdisk";
236 EXPECT_THAT(generic_ramdisk_allow_list, IsSupersetOf(actual_files))
237 << "Contains files disallowed by " << (is_debuggable ? "debuggable " : "")
238 << "generic ramdisk";
239 }
240