Merge "microdroid: init.rc: add tracefs for debug boot"
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 5cd4af8..5d36f16 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -16,6 +16,8 @@
package com.android.virt.fs;
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -40,7 +42,9 @@
import org.junit.After;
import org.junit.AssumptionViolatedException;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import java.util.Optional;
@@ -100,6 +104,9 @@
private ExecutorService mThreadPool = Executors.newCachedThreadPool();
+ @Rule public TestLogData mTestLogs = new TestLogData();
+ @Rule public TestName mTestName = new TestName();
+
@BeforeClassWithInfo
public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
assertNotNull(testInfo.getDevice());
@@ -170,10 +177,18 @@
@After
public void tearDown() throws Exception {
sAndroid.tryRun("killall fd_server");
- sAndroid.run("rm -rf " + TEST_OUTPUT_DIR);
-
tryRunOnMicrodroid("killall authfs");
tryRunOnMicrodroid("umount " + MOUNT_DIR);
+
+ // Even though we only run one VM for the whole class, and could have collect the VM log
+ // after all tests are done, TestLogData doesn't seem to work at class level. Hence,
+ // collect recent logs manually for each test method.
+ String vmRecentLog = TEST_OUTPUT_DIR + "/vm_recent.log";
+ sAndroid.tryRun("tail -n 50 " + LOG_PATH + " > " + vmRecentLog);
+ archiveLogThenDelete(mTestLogs, getDevice(), vmRecentLog,
+ "vm_recent.log-" + mTestName.getMethodName());
+
+ sAndroid.run("rm -rf " + TEST_OUTPUT_DIR);
}
@Test
diff --git a/compos/tests/java/android/compos/test/ComposTestCase.java b/compos/tests/java/android/compos/test/ComposTestCase.java
index cfe72cb..6773eb7 100644
--- a/compos/tests/java/android/compos/test/ComposTestCase.java
+++ b/compos/tests/java/android/compos/test/ComposTestCase.java
@@ -16,6 +16,8 @@
package android.compos.test;
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+
import static com.google.common.truth.Truth.assertThat;
import android.platform.test.annotations.RootPermissionTest;
@@ -28,7 +30,9 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
@RootPermissionTest
@@ -41,6 +45,8 @@
private static final String COMPOS_VERIFY_BIN =
"/apex/com.android.compos/bin/compos_verify";
+ private static final String COMPOS_APEXDATA_DIR = "/data/misc/apexdata/com.android.compos";
+
/** Output directory of odrefresh */
private static final String TEST_ARTIFACTS_DIR = "test-artifacts";
@@ -61,6 +67,9 @@
"dalvik.vm.systemservercompilerfilter";
private String mBackupSystemServerCompilerFilter;
+ @Rule public TestLogData mTestLogs = new TestLogData();
+ @Rule public TestName mTestName = new TestName();
+
@Before
public void setUp() throws Exception {
testIfDeviceIsCapable(getDevice());
@@ -77,6 +86,11 @@
public void tearDown() throws Exception {
killVmAndReconnectAdb();
+ archiveLogThenDelete(mTestLogs, getDevice(), COMPOS_APEXDATA_DIR + "/vm_console.log",
+ "vm_console.log-" + mTestName.getMethodName());
+ archiveLogThenDelete(mTestLogs, getDevice(), COMPOS_APEXDATA_DIR + "/vm.log",
+ "vm.log-" + mTestName.getMethodName());
+
CommandRunner android = new CommandRunner(getDevice());
// Clear up any CompOS instance files we created
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 184b9ff..7b77c18 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -22,7 +22,8 @@
use compos_aidl_interface::binder::ProcessState;
use compos_common::compos_client::{VmInstance, VmParameters};
use compos_common::odrefresh::{
- CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR, TEST_ARTIFACTS_SUBDIR,
+ CURRENT_ARTIFACTS_SUBDIR, ODREFRESH_OUTPUT_ROOT_DIR, PENDING_ARTIFACTS_SUBDIR,
+ TEST_ARTIFACTS_SUBDIR,
};
use compos_common::{
COMPOS_DATA_ROOT, CURRENT_INSTANCE_DIR, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE,
@@ -62,7 +63,7 @@
.long("instance")
.takes_value(true)
.required(true)
- .possible_values(&["current", "test"]),
+ .possible_values(&["current", "pending", "test"]),
)
.arg(clap::Arg::with_name("debug").long("debug"))
.get_matches();
@@ -70,6 +71,7 @@
let debug_mode = matches.is_present("debug");
let (instance_dir, artifacts_dir) = match matches.value_of("instance").unwrap() {
"current" => (CURRENT_INSTANCE_DIR, CURRENT_ARTIFACTS_SUBDIR),
+ "pending" => (CURRENT_INSTANCE_DIR, PENDING_ARTIFACTS_SUBDIR),
"test" => (TEST_INSTANCE_DIR, TEST_ARTIFACTS_SUBDIR),
_ => unreachable!("Unexpected instance name"),
};
@@ -90,8 +92,8 @@
let info = artifacts_dir.join("compos.info");
let signature = artifacts_dir.join("compos.info.signature");
- let info = read_small_file(&info)?;
- let signature = read_small_file(&signature)?;
+ let info = read_small_file(&info).context("Failed to read compos.info")?;
+ let signature = read_small_file(&signature).context("Failed to read compos.info signature")?;
// We need to start the thread pool to be able to receive Binder callbacks
ProcessState::start_thread_pool();
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index ac5d38b..f598034 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -90,7 +90,7 @@
```shell
banchan com.android.virt aosp_arm64 // or aosp_x86_64 if the device is cuttlefish
-m apps_only dist
+UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true m apps_only dist
adb install out/dist/com.android.virt.apex
adb reboot
```
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index a93a801..3ae4b1a 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -212,6 +212,7 @@
microdroid_boot_cmdline = [
"panic=-1",
"bootconfig",
+ "ioremap_guard",
]
bootimg {
diff --git a/microdroid/README.md b/microdroid/README.md
index 783f300..a652139 100644
--- a/microdroid/README.md
+++ b/microdroid/README.md
@@ -31,7 +31,7 @@
```sh
banchan com.android.virt aosp_arm64
-UNBUNDLED_BUILD_SDKS_FROM_SOURCES=true m apps_only dist
+UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true m apps_only dist
adb install out/dist/com.android.virt.apex
adb reboot
```
diff --git a/microdroid/dice/service.rs b/microdroid/dice/service.rs
index 8cb5cc3..8199c7c 100644
--- a/microdroid/dice/service.rs
+++ b/microdroid/dice/service.rs
@@ -30,6 +30,7 @@
use std::slice;
use std::sync::Arc;
+const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
const DICE_HAL_SERVICE_NAME: &str = "android.hardware.security.dice.IDiceDevice/default";
/// Artifacts that are mapped into the process address space from the driver.
@@ -135,16 +136,19 @@
#[derive(Clone, Serialize, Deserialize)]
enum DriverArtifactManager {
+ Invalid,
Driver(PathBuf),
Updated(RawArtifacts),
}
impl DriverArtifactManager {
fn new(driver_path: &Path) -> Self {
- // TODO(218793274): fail if driver is missing in protected mode
if driver_path.exists() {
log::info!("Using DICE values from driver");
Self::Driver(driver_path.to_path_buf())
+ } else if Path::new(AVF_STRICT_BOOT).exists() {
+ log::error!("Strict boot requires DICE value from driver but none were found");
+ Self::Invalid
} else {
log::warn!("Using sample DICE values");
let (cdi_attest, cdi_seal, bcc) = diced_sample_inputs::make_sample_bcc_and_cdis()
@@ -164,11 +168,15 @@
F: FnOnce(&dyn DiceArtifacts) -> Result<T>,
{
match self {
+ Self::Invalid => bail!("No DICE artifacts available."),
Self::Driver(driver_path) => f(&MappedDriverArtifacts::new(driver_path.as_path())?),
Self::Updated(raw_artifacts) => f(raw_artifacts),
}
}
fn update(self, new_artifacts: &impl DiceArtifacts) -> Result<Self> {
+ if let Self::Invalid = self {
+ bail!("Cannot update invalid DICE artifacts.");
+ }
if let Self::Driver(driver_path) = self {
// Writing to the device wipes the artifcates. The string is ignored
// by the driver but included for documentation.
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index b644285..9e159d2 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -61,6 +61,8 @@
const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
+const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
+const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
/// The CID representing the host VM
const VMADDR_CID_HOST: u32 = 2;
@@ -193,12 +195,35 @@
Ok(())
}
+fn is_strict_boot() -> bool {
+ Path::new(AVF_STRICT_BOOT).exists()
+}
+
+fn is_new_instance() -> bool {
+ Path::new(AVF_NEW_INSTANCE).exists()
+}
+
fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
let metadata = load_metadata().context("Failed to load payload metadata")?;
let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
+ if is_strict_boot() {
+ // Provisioning must happen on the first boot and never again.
+ if is_new_instance() {
+ ensure!(
+ saved_data.is_none(),
+ MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
+ );
+ } else {
+ ensure!(
+ saved_data.is_some(),
+ MicrodroidError::InvalidConfig("Instance data not found.".to_string())
+ );
+ };
+ }
+
// Verify the payload before using it.
let verified_data =
verify_payload(&metadata, saved_data.as_ref()).context("Payload verification failed")?;
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index 8e86fd1..40be248 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -16,6 +16,8 @@
package android.virt.test;
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -28,6 +30,8 @@
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.TestDevice;
import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.LogDataType;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
@@ -46,6 +50,7 @@
public abstract class VirtualizationTestCaseBase extends BaseHostJUnit4Test {
protected static final String TEST_ROOT = "/data/local/tmp/virt/";
protected static final String VIRT_APEX = "/apex/com.android.virt/";
+ protected static final String LOG_PATH = TEST_ROOT + "log.txt";
private static final int TEST_VM_ADB_PORT = 8000;
private static final String MICRODROID_SERIAL = "localhost:" + TEST_VM_ADB_PORT;
private static final String INSTANCE_IMG = "instance.img";
@@ -105,6 +110,16 @@
assumeTrue("Requires VM support", testDevice.supportsMicrodroid());
}
+ public static void archiveLogThenDelete(TestLogData logs, ITestDevice device, String remotePath,
+ String localName) throws DeviceNotAvailableException {
+ File logFile = device.pullFile(remotePath);
+ if (logFile != null) {
+ logs.addTestLog(localName, LogDataType.TEXT, new FileInputStreamSource(logFile));
+ // Delete to avoid confusing logs from a previous run, just in case.
+ device.deleteFile(remotePath);
+ }
+ }
+
// Run an arbitrary command in the host side and returns the result
private static String runOnHost(String... cmd) {
return runOnHostWithTimeout(10000, cmd);
@@ -270,7 +285,7 @@
final String outApkIdsigPath = TEST_ROOT + apkName + ".idsig";
final String instanceImg = TEST_ROOT + INSTANCE_IMG;
- final String logPath = TEST_ROOT + "log.txt";
+ final String logPath = LOG_PATH;
final String debugFlag = debug ? "--debug full" : "";
// Run the VM
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 51d62cb..f305372 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -16,6 +16,8 @@
package android.virt.test;
+import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
@@ -40,7 +42,9 @@
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import java.io.File;
@@ -65,6 +69,9 @@
private static final int NUM_VCPUS = 3;
private static final String CPU_AFFINITY = "0,1,2";
+ @Rule public TestLogData mTestLogs = new TestLogData();
+ @Rule public TestName mTestName = new TestName();
+
// TODO(b/176805428): remove this
private boolean isCuttlefish() throws Exception {
String productName = getDevice().getProperty("ro.product.name");
@@ -257,7 +264,7 @@
final String configPath = TEST_ROOT + "raw_config.json";
getDevice().pushString(config.toString(), configPath);
- final String logPath = TEST_ROOT + "log";
+ final String logPath = LOG_PATH;
final String ret = android.runWithTimeout(
60 * 1000,
VIRT_APEX + "bin/vm run",
@@ -436,6 +443,7 @@
public void shutdown() throws Exception {
cleanUpVirtualizationTestSetup(getDevice());
- getDevice().uninstallPackage(PACKAGE_NAME);
+ archiveLogThenDelete(mTestLogs, getDevice(), LOG_PATH,
+ "vm.log-" + mTestName.getMethodName());
}
}