1 /*
2  * Copyright (C) 2023 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 <android-base/properties.h>
18 #include <android/api-level.h>
19 #include <elf.h>
20 #include <gtest/gtest.h>
21 #include <libelf64/parse.h>
22 
23 class Vts16KPageSizeTest : public ::testing::Test {
24   protected:
VendorApiLevel()25     static int VendorApiLevel() {
26         // "ro.vendor.api_level" is added in Android T.
27         // Undefined indicates S or below
28         return android::base::GetIntProperty("ro.vendor.api_level", __ANDROID_API_S__);
29     }
30 
NoBionicPageSizeMacroProperty()31     static bool NoBionicPageSizeMacroProperty() {
32         // "ro.product.build.no_bionic_page_size_macro" was added in Android V and is
33         // set to true when Android is build with PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true.
34         return android::base::GetBoolProperty("ro.product.build.no_bionic_page_size_macro", false);
35     }
36 
Architecture()37     static std::string Architecture() { return android::base::GetProperty("ro.bionic.arch", ""); }
38 
MaxPageSize(const std::string & filepath)39     static ssize_t MaxPageSize(const std::string& filepath) {
40         ssize_t maxPageSize = -1;
41 
42         android::elf64::Elf64Binary elf;
43 
44         if (!android::elf64::Elf64Parser::ParseElfFile(filepath, elf)) {
45             return -1;
46         }
47 
48         for (int i = 0; i < elf.phdrs.size(); i++) {
49             Elf64_Phdr phdr = elf.phdrs[i];
50 
51             if ((phdr.p_type != PT_LOAD) || !(phdr.p_type & PF_X)) {
52                 continue;
53             }
54 
55             maxPageSize = phdr.p_align;
56             break;
57         }
58 
59         return maxPageSize;
60     }
61 
SetUpTestSuite()62     static void SetUpTestSuite() {
63         if (VendorApiLevel() < __ANDROID_API_V__) {
64             GTEST_SKIP() << "16kB support is only required on V and later releases.";
65         }
66     }
67 
68     /*
69      * x86_64 also needs to be at least 16KB aligned, since Android
70      * supports page size emulation in x86_64 for app development.
71      */
RequiredMaxPageSize()72     size_t RequiredMaxPageSize() {
73         if (mArch == "arm64" || mArch == "aarch64" || mArch == "x86_64") {
74             return 0x4000;
75         } else {
76             return 0x1000;
77         }
78     }
79 
80     const std::string mArch = Architecture();
81 };
82 
83 /**
84  * Checks the max-page-size of init against the architecture's
85  * required max-page-size.
86  *
87  * Note: a more comprehensive version of this test exists in
88  * elf_alignment_test. This has turned out to be a canary test
89  * to give visibility on this when checking all 16K tests.
90  */
91 // @VsrTest = 3.14.1
TEST_F(Vts16KPageSizeTest,InitMaxPageSizeTest)92 TEST_F(Vts16KPageSizeTest, InitMaxPageSizeTest) {
93     constexpr char initPath[] = "/system/bin/init";
94 
95     ssize_t expectedMaxPageSize = RequiredMaxPageSize();
96     ASSERT_NE(expectedMaxPageSize, -1)
97             << "Failed to get required max page size for arch: " << mArch;
98 
99     ssize_t initMaxPageSize = MaxPageSize(initPath);
100     ASSERT_NE(initMaxPageSize, -1) << "Failed to get max page size of ELF: " << initPath;
101 
102     ASSERT_EQ(initMaxPageSize % expectedMaxPageSize, 0)
103             << "ELF " << initPath << " with page size " << initMaxPageSize
104             << " was not built with the required max-page-size " << expectedMaxPageSize;
105 }
106 
107 /**
108  * Checks if the vendor's build was compiled with the define
109  * PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO based on the product property
110  * ro.product.build.no_bionic_page_size_macro.
111  */
112 // @VsrTest = 3.14.2
TEST_F(Vts16KPageSizeTest,NoBionicPageSizeMacro)113 TEST_F(Vts16KPageSizeTest, NoBionicPageSizeMacro) {
114     /**
115      * TODO(b/315034809): switch to error when final decision is made.
116      */
117     if (!NoBionicPageSizeMacroProperty())
118         GTEST_SKIP() << "Device was not built with: PRODUCT_NO_BIONIC_PAGE_SIZE_MACRO := true";
119 }
120