1 /*
2  * Copyright 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 package com.android.pvmfw.test;
18 
19 import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assume.assumeTrue;
24 
25 import androidx.annotation.NonNull;
26 import androidx.annotation.Nullable;
27 
28 import com.android.microdroid.test.host.MicrodroidHostTestCaseBase;
29 import com.android.tradefed.device.DeviceNotAvailableException;
30 import com.android.tradefed.device.ITestDevice;
31 import com.android.tradefed.device.TestDevice;
32 import com.android.tradefed.util.FileUtil;
33 
34 import org.junit.After;
35 import org.junit.Before;
36 
37 import java.io.File;
38 import java.util.Map;
39 
40 /** Base class for testing custom pvmfw */
41 public class CustomPvmfwHostTestCaseBase extends MicrodroidHostTestCaseBase {
42     @NonNull public static final String PVMFW_FILE_NAME = "pvmfw_test.bin";
43     @NonNull public static final String BCC_FILE_NAME = "bcc.dat";
44     @NonNull public static final String PACKAGE_FILE_NAME = "MicrodroidTestApp.apk";
45     @NonNull public static final String PACKAGE_NAME = "com.android.microdroid.test";
46     @NonNull public static final String MICRODROID_DEBUG_FULL = "full";
47     @NonNull public static final String MICRODROID_DEBUG_NONE = "none";
48 
49     @NonNull public static final String MICRODROID_CONFIG_PATH = "assets/vm_config_apex.json";
50 
51     @NonNull
52     public static final String VM_REFERENCE_DT_PATH = "/data/local/tmp/pvmfw/reference_dt.dtb";
53 
54     @NonNull public static final String MICRODROID_LOG_PATH = TEST_ROOT + "log.txt";
55     public static final int BOOT_COMPLETE_TIMEOUT_MS = 30000; // 30 seconds
56     public static final int BOOT_FAILURE_WAIT_TIME_MS = 10000; // 10 seconds
57     public static final int CONSOLE_OUTPUT_WAIT_MS = 5000; // 5 seconds
58 
59     @NonNull public static final String CUSTOM_PVMFW_FILE_PREFIX = "pvmfw";
60     @NonNull public static final String CUSTOM_PVMFW_FILE_SUFFIX = ".bin";
61 
62     @NonNull
63     public static final String CUSTOM_PVMFW_IMG_PATH = TRADEFED_TEST_ROOT + PVMFW_FILE_NAME;
64 
65     @NonNull public static final String CUSTOM_PVMFW_IMG_PATH_PROP = "hypervisor.pvmfw.path";
66 
67     @Nullable private File mPvmfwBinFileOnHost;
68     @Nullable private File mBccFileOnHost;
69     @Nullable private File mVmReferenceDtFile;
70     private boolean mSecretKeeperSupported;
71 
72     @NonNull private TestDevice mAndroidDevice;
73     @Nullable private ITestDevice mMicrodroidDevice;
74 
75     @Nullable private File mCustomPvmfwFileOnHost;
76 
77     @Before
setUp()78     public void setUp() throws Exception {
79         mAndroidDevice = (TestDevice) getDevice();
80 
81         // Check device capabilities
82         assumeDeviceIsCapable(mAndroidDevice);
83         assumeTrue(
84                 "Skip if protected VMs are not supported",
85                 mAndroidDevice.supportsMicrodroid(/* protectedVm= */ true));
86 
87         mPvmfwBinFileOnHost = findTestFile(PVMFW_FILE_NAME);
88         mBccFileOnHost = findTestFile(BCC_FILE_NAME);
89 
90         // This is prepared by AndroidTest.xml
91         mVmReferenceDtFile = mAndroidDevice.pullFile(VM_REFERENCE_DT_PATH);
92 
93         mSecretKeeperSupported = isUpdatableVmSupported();
94 
95         // Prepare for system properties for custom pvmfw.img.
96         // File will be prepared later in individual test and then pushed to device
97         // when launching with launchProtectedVmAndWaitForBootCompleted().
98         mCustomPvmfwFileOnHost =
99                 FileUtil.createTempFile(CUSTOM_PVMFW_FILE_PREFIX, CUSTOM_PVMFW_FILE_SUFFIX);
100         setPropertyOrThrow(mAndroidDevice, CUSTOM_PVMFW_IMG_PATH_PROP, CUSTOM_PVMFW_IMG_PATH);
101 
102         // Prepare for launching microdroid
103         mAndroidDevice.installPackage(findTestFile(PACKAGE_FILE_NAME), /* reinstall */ false);
104         prepareVirtualizationTestSetup(mAndroidDevice);
105         mMicrodroidDevice = null;
106     }
107 
108     @After
shutdown()109     public void shutdown() throws Exception {
110         shutdownMicrodroid();
111 
112         mAndroidDevice.uninstallPackage(PACKAGE_NAME);
113 
114         FileUtil.deleteFile(mVmReferenceDtFile);
115 
116         // Cleanup for custom pvmfw.img
117         setPropertyOrThrow(mAndroidDevice, CUSTOM_PVMFW_IMG_PATH_PROP, "");
118         FileUtil.deleteFile(mCustomPvmfwFileOnHost);
119 
120         cleanUpVirtualizationTestSetup(mAndroidDevice);
121     }
122 
123     /** Returns android device */
124     @NonNull
getAndroidDevice()125     public TestDevice getAndroidDevice() {
126         return mAndroidDevice;
127     }
128 
129     /** Returns pvmfw.bin file on host for building custom pvmfw with */
130     @NonNull
getPvmfwBinFile()131     public File getPvmfwBinFile() {
132         return mPvmfwBinFileOnHost;
133     }
134 
135     /** Returns BCC file on host for building custom pvmfw with */
136     @NonNull
getBccFile()137     public File getBccFile() {
138         return mBccFileOnHost;
139     }
140 
141     /** Returns VM reference DT, generated from DUT, on host for building custom pvmfw with. */
142     @Nullable
getVmReferenceDtFile()143     public File getVmReferenceDtFile() {
144         return mVmReferenceDtFile;
145     }
146 
147     /**
148      * Returns a custom pvmfw file.
149      *
150      * <p>This is a temporary file on host. The file should been prepared as a custom pvmfw because
151      * calling {@link #launchProtectedVmAndWaitForBootCompleted}, so virtualization manager can read
152      * the file path from sysprop and boot pVM with it.
153      */
154     @NonNull
getCustomPvmfwFile()155     public File getCustomPvmfwFile() {
156         return mCustomPvmfwFileOnHost;
157     }
158 
159     /**
160      * Returns whether a secretkeeper is supported.
161      *
162      * <p>If {@code true}, then VM reference DT must exist. (i.e. {@link #getVmReferenceDtFile} must
163      * exist {@code null}).
164      */
isSecretKeeperSupported()165     public boolean isSecretKeeperSupported() {
166         return mSecretKeeperSupported;
167     }
168 
169     /**
170      * Launches protected VM with custom pvmfw ({@link #getCustomPvmfwFile}) and wait for boot
171      * completed. Throws exception when boot failed.
172      */
launchProtectedVmAndWaitForBootCompleted( String debugLevel, long adbTimeoutMs, @NonNull Map<String, File> bootFiles)173     public ITestDevice launchProtectedVmAndWaitForBootCompleted(
174             String debugLevel, long adbTimeoutMs, @NonNull Map<String, File> bootFiles)
175             throws DeviceNotAvailableException {
176         MicrodroidBuilder builder =
177                 MicrodroidBuilder.fromDevicePath(
178                                 getPathForPackage(PACKAGE_NAME), MICRODROID_CONFIG_PATH)
179                         .debugLevel(debugLevel)
180                         .protectedVm(/* protectedVm= */ true)
181                         .addBootFile(mCustomPvmfwFileOnHost, PVMFW_FILE_NAME)
182                         .setAdbConnectTimeoutMs(adbTimeoutMs);
183         for (String name : bootFiles.keySet()) {
184             File file = bootFiles.get(name);
185             builder.addBootFile(file, name);
186         }
187 
188         mMicrodroidDevice = builder.build(mAndroidDevice);
189 
190         assertThat(mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT_MS)).isTrue();
191         assertThat(mMicrodroidDevice.enableAdbRoot()).isTrue();
192         return mMicrodroidDevice;
193     }
194 
195     /** Shuts down microdroid if it's running */
shutdownMicrodroid()196     public void shutdownMicrodroid() throws Exception {
197         if (mMicrodroidDevice != null) {
198             mAndroidDevice.shutdownMicrodroid(mMicrodroidDevice);
199             mMicrodroidDevice = null;
200         }
201     }
202 }
203