Merge changes I36abf21b,Iede88046
* changes:
Move APEX handling to a library
Migrate payload/apex.rs to thiserror
diff --git a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
index 31f8458..749f3c1 100644
--- a/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/java/src/com/android/fs/AuthFsHostTest.java
@@ -16,6 +16,7 @@
package com.android.virt.fs;
+import static android.virt.test.CommandResultSubject.assertThat;
import static android.virt.test.LogArchiver.archiveLogThenDelete;
import static com.android.tradefed.device.TestDevice.MicrodroidBuilder;
@@ -24,9 +25,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@@ -46,7 +45,6 @@
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
import org.junit.After;
import org.junit.Before;
@@ -255,7 +253,7 @@
runAuthFsOnMicrodroid("--remote-ro-file 3:" + DIGEST_4M + " --cid " + VMADDR_CID_HOST);
// Verify
- assertFalse(copyFile(sMicrodroid, MOUNT_DIR + "/3", "/dev/null"));
+ assertThat(copyFile(sMicrodroid, MOUNT_DIR + "/3", "/dev/null")).isFailed();
}
@Test
@@ -268,7 +266,7 @@
String srcPath = "/system/bin/linker64";
String destPath = MOUNT_DIR + "/3";
String backendPath = TEST_OUTPUT_DIR + "/out.file";
- assertTrue(copyFile(sMicrodroid, srcPath, destPath));
+ assertThat(copyFile(sMicrodroid, srcPath, destPath)).isSuccess();
// Verify
String expectedHash = computeFileHash(sMicrodroid, srcPath);
@@ -284,32 +282,36 @@
String srcPath = "/system/bin/linker64";
String destPath = MOUNT_DIR + "/3";
String backendPath = TEST_OUTPUT_DIR + "/out.file";
- assertTrue(copyFile(sMicrodroid, srcPath, destPath));
+ assertThat(copyFile(sMicrodroid, srcPath, destPath)).isSuccess();
// Action
// Tampering with the first 2 4K-blocks of the backing file.
- assertTrue(
+ assertThat(
writeZerosAtFileOffset(sAndroid, backendPath,
- /* offset */ 0, /* number */ 8192, /* writeThrough */ false));
+ /* offset */ 0, /* number */ 8192, /* writeThrough */ false))
+ .isSuccess();
// Verify
// Write to a block partially requires a read back to calculate the new hash. It should fail
// when the content is inconsistent to the known hash. Use direct I/O to avoid simply
// writing to the filesystem cache.
- assertFalse(
+ assertThat(
writeZerosAtFileOffset(sMicrodroid, destPath,
- /* offset */ 0, /* number */ 1024, /* writeThrough */ true));
+ /* offset */ 0, /* number */ 1024, /* writeThrough */ true))
+ .isFailed();
// A full 4K write does not require to read back, so write can succeed even if the backing
// block has already been tampered.
- assertTrue(
+ assertThat(
writeZerosAtFileOffset(sMicrodroid, destPath,
- /* offset */ 4096, /* number */ 4096, /* writeThrough */ false));
+ /* offset */ 4096, /* number */ 4096, /* writeThrough */ false))
+ .isSuccess();
// Otherwise, a partial write with correct backing file should still succeed.
- assertTrue(
+ assertThat(
writeZerosAtFileOffset(sMicrodroid, destPath,
- /* offset */ 8192, /* number */ 1024, /* writeThrough */ false));
+ /* offset */ 8192, /* number */ 1024, /* writeThrough */ false))
+ .isSuccess();
}
@Test
@@ -321,20 +323,23 @@
String srcPath = "/system/bin/linker64";
String destPath = MOUNT_DIR + "/3";
String backendPath = TEST_OUTPUT_DIR + "/out.file";
- assertTrue(copyFile(sMicrodroid, srcPath, destPath));
+ assertThat(copyFile(sMicrodroid, srcPath, destPath)).isSuccess();
// Action
// Tampering with the first 4K-block of the backing file.
- assertTrue(
+ assertThat(
writeZerosAtFileOffset(sAndroid, backendPath,
- /* offset */ 0, /* number */ 4096, /* writeThrough */ false));
+ /* offset */ 0, /* number */ 4096, /* writeThrough */ false))
+ .isSuccess();
// Verify
// Force dropping the page cache, so that the next read can be validated.
sMicrodroid.run("echo 1 > /proc/sys/vm/drop_caches");
// A read will fail if the backing data has been tampered.
- assertFalse(checkReadAt(sMicrodroid, destPath, /* offset */ 0, /* number */ 4096));
- assertTrue(checkReadAt(sMicrodroid, destPath, /* offset */ 4096, /* number */ 4096));
+ assertThat(checkReadAt(sMicrodroid, destPath, /* offset */ 0, /* number */ 4096))
+ .isFailed();
+ assertThat(checkReadAt(sMicrodroid, destPath, /* offset */ 4096, /* number */ 4096))
+ .isSuccess();
}
@Test
@@ -349,15 +354,16 @@
// Action
// Tampering with the last 4K-block of the backing file.
- assertTrue(
+ assertThat(
writeZerosAtFileOffset(sAndroid, backendPath,
- /* offset */ 4096, /* number */ 1, /* writeThrough */ false));
+ /* offset */ 4096, /* number */ 1, /* writeThrough */ false))
+ .isSuccess();
// Verify
// A resize (to a non-multiple of 4K) will fail if the last backing chunk has been
// tampered. The original data is necessary (and has to be verified) to calculate the new
// hash with shorter data.
- assertFalse(resizeFile(sMicrodroid, outputPath, 8000));
+ assertThat(resizeFile(sMicrodroid, outputPath, 8000)).isFailed();
}
@Test
@@ -376,14 +382,14 @@
backendPath,
"684ad25fdc2bbb80cbc910dd1bde6d5499ccf860ca6ee44704b77ec445271353");
- assertTrue(resizeFile(sMicrodroid, outputPath, 15000));
+ assertThat(resizeFile(sMicrodroid, outputPath, 15000)).isSuccess();
assertEquals(getFileSizeInBytes(sMicrodroid, outputPath), 15000);
expectBackingFileConsistency(
outputPath,
backendPath,
"567c89f62586e0d33369157afdfe99a2fa36cdffb01e91dcdc0b7355262d610d");
- assertTrue(resizeFile(sMicrodroid, outputPath, 5000));
+ assertThat(resizeFile(sMicrodroid, outputPath, 5000)).isSuccess();
assertEquals(getFileSizeInBytes(sMicrodroid, outputPath), 5000);
expectBackingFileConsistency(
outputPath,
@@ -412,7 +418,7 @@
"684ad25fdc2bbb80cbc910dd1bde6d5499ccf860ca6ee44704b77ec445271353");
// Regular file operations work, e.g. resize.
- assertTrue(resizeFile(sMicrodroid, authfsPath, 15000));
+ assertThat(resizeFile(sMicrodroid, authfsPath, 15000)).isSuccess();
assertEquals(getFileSizeInBytes(sMicrodroid, authfsPath), 15000);
expectBackingFileConsistency(
authfsPath,
@@ -512,7 +518,7 @@
sMicrodroid.run("test ! -d " + authfsOutputDir + "/dir/dir2");
sAndroid.run("test ! -d " + androidOutputDir + "/dir/dir2");
// Can only delete a directory if empty
- assertFailed(sMicrodroid, "rmdir " + authfsOutputDir + "/dir");
+ assertThat(sMicrodroid.runForResult("rmdir " + authfsOutputDir + "/dir")).isFailed();
sMicrodroid.run("test -d " + authfsOutputDir + "/dir"); // still there
sMicrodroid.run("rm " + authfsOutputDir + "/dir/file");
sMicrodroid.run("rmdir " + authfsOutputDir + "/dir");
@@ -536,10 +542,12 @@
// Action & Verify
// Cannot create directory if an entry with the same name already exists.
- assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_file");
- assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_dir");
- assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_dir/file");
- assertFailed(sMicrodroid, "mkdir " + authfsOutputDir + "/some_dir/dir");
+ assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_file")).isFailed();
+ assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir")).isFailed();
+ assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir/file"))
+ .isFailed();
+ assertThat(sMicrodroid.runForResult("mkdir " + authfsOutputDir + "/some_dir/dir"))
+ .isFailed();
}
@Test
@@ -603,7 +611,8 @@
// Verify
sMicrodroid.run("test -f " + authfsInputDir + "/system/framework/services.jar");
- assertFailed(sMicrodroid, "test -f " + authfsInputDir + "/system/bin/sh");
+ assertThat(sMicrodroid.runForResult("test -f " + authfsInputDir + "/system/bin/sh"))
+ .isFailed();
}
@Test
@@ -658,8 +667,8 @@
sMicrodroid.run("chmod 321 " + MOUNT_DIR + "/3");
expectFileMode("--wx-w---x", MOUNT_DIR + "/3", TEST_OUTPUT_DIR + "/file");
// Can't set the disallowed bits
- assertFailed(sMicrodroid, "chmod +s " + MOUNT_DIR + "/3");
- assertFailed(sMicrodroid, "chmod +t " + MOUNT_DIR + "/3");
+ assertThat(sMicrodroid.runForResult("chmod +s " + MOUNT_DIR + "/3")).isFailed();
+ assertThat(sMicrodroid.runForResult("chmod +t " + MOUNT_DIR + "/3")).isFailed();
}
@Test
@@ -681,8 +690,9 @@
sMicrodroid.run("chmod 321 " + authfsOutputDir + "/dir");
expectFileMode("d-wx-w---x", authfsOutputDir + "/dir", TEST_OUTPUT_DIR + "/dir");
// Can't set the disallowed bits
- assertFailed(sMicrodroid, "chmod +s " + authfsOutputDir + "/dir/dir2");
- assertFailed(sMicrodroid, "chmod +t " + authfsOutputDir + "/dir");
+ assertThat(sMicrodroid.runForResult("chmod +s " + authfsOutputDir + "/dir/dir2"))
+ .isFailed();
+ assertThat(sMicrodroid.runForResult("chmod +t " + authfsOutputDir + "/dir")).isFailed();
}
@Test
@@ -704,8 +714,8 @@
sMicrodroid.run("chmod 321 " + authfsOutputDir + "/file2");
expectFileMode("--wx-w---x", authfsOutputDir + "/file2", TEST_OUTPUT_DIR + "/file2");
// Can't set the disallowed bits
- assertFailed(sMicrodroid, "chmod +s " + authfsOutputDir + "/file");
- assertFailed(sMicrodroid, "chmod +t " + authfsOutputDir + "/file2");
+ assertThat(sMicrodroid.runForResult("chmod +s " + authfsOutputDir + "/file")).isFailed();
+ assertThat(sMicrodroid.runForResult("chmod +t " + authfsOutputDir + "/file2")).isFailed();
}
@Test
@@ -752,13 +762,12 @@
}
}
- private static boolean copyFile(CommandRunner runner, String src, String dest)
+ private static CommandResult copyFile(CommandRunner runner, String src, String dest)
throws DeviceNotAvailableException {
// toybox's cp(1) implementation ignores most read(2) errors, and it's unclear what the
// canonical behavior should be (not mentioned in manpage). For this test, use cat(1) in
// order to fail on I/O error.
- CommandResult result = runner.runForResult("cat " + src + " > " + dest);
- return result.getStatus() == CommandStatus.SUCCESS;
+ return runner.runForResult("cat " + src + " > " + dest);
}
private void expectFileMode(String expected, String microdroidPath, String androidPath)
@@ -770,10 +779,9 @@
assertEquals("Inconsistent mode for " + androidPath + " (android)", expected, actual);
}
- private static boolean resizeFile(CommandRunner runner, String path, long size)
+ private static CommandResult resizeFile(CommandRunner runner, String path, long size)
throws DeviceNotAvailableException {
- CommandResult result = runner.runForResult("truncate -c -s " + size + " " + path);
- return result.getStatus() == CommandStatus.SUCCESS;
+ return runner.runForResult("truncate -c -s " + size + " " + path);
}
private static long getFileSizeInBytes(CommandRunner runner, String path)
@@ -787,19 +795,17 @@
"yes $'\\x01' | tr -d '\\n' | dd bs=1 count=" + numberOfOnes + " of=" + filePath);
}
- private static boolean checkReadAt(CommandRunner runner, String filePath, long offset,
+ private static CommandResult checkReadAt(CommandRunner runner, String filePath, long offset,
long size) throws DeviceNotAvailableException {
String cmd = "dd if=" + filePath + " of=/dev/null bs=1 count=" + size;
if (offset > 0) {
cmd += " skip=" + offset;
}
- CommandResult result = runner.runForResult(cmd);
- return result.getStatus() == CommandStatus.SUCCESS;
+ return runner.runForResult(cmd);
}
- private static boolean writeZerosAtFileOffset(CommandRunner runner, String filePath,
- long offset, long numberOfZeros, boolean writeThrough)
- throws DeviceNotAvailableException {
+ private CommandResult writeZerosAtFileOffset(CommandRunner runner, String filePath, long offset,
+ long numberOfZeros, boolean writeThrough) throws DeviceNotAvailableException {
String cmd = "dd if=/dev/zero of=" + filePath + " bs=1 count=" + numberOfZeros
+ " conv=notrunc";
if (offset > 0) {
@@ -808,14 +814,7 @@
if (writeThrough) {
cmd += " direct";
}
- CommandResult result = runner.runForResult(cmd);
- return result.getStatus() == CommandStatus.SUCCESS;
- }
-
- private static void assertFailed(CommandRunner runner, String... cmd)
- throws DeviceNotAvailableException {
- CommandResult result = runner.runForResult(cmd);
- assertThat(result.getStatus()).isEqualTo(CommandStatus.FAILED);
+ return runner.runForResult(cmd);
}
private void runAuthFsOnMicrodroid(String flags) {
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index a2ae144..8702568 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -157,16 +157,34 @@
genrule {
name: "microdroid_build_prop_gen_x86_64",
- srcs: ["build.prop"],
+ srcs: [
+ "build.prop",
+ ":buildinfo.prop",
+ ],
out: ["build.prop.out"],
- cmd: "cp $(in) $(out); echo ro.product.cpu.abilist=x86_64 >> $(out)",
+ cmd: "(echo '# build properties from buildinfo.prop module' && " +
+ "grep ro\\.build\\.version\\.codename= $(location :buildinfo.prop) && " +
+ "grep ro\\.build\\.version\\.release= $(location :buildinfo.prop) && " +
+ "grep ro\\.build\\.version\\.sdk= $(location :buildinfo.prop) && " +
+ "grep ro\\.build\\.version\\.security_patch= $(location :buildinfo.prop) && " +
+ "cat $(location build.prop) && " +
+ "echo ro.product.cpu.abilist=x86_64) > $(out)",
}
genrule {
name: "microdroid_build_prop_gen_arm64",
- srcs: ["build.prop"],
+ srcs: [
+ "build.prop",
+ ":buildinfo.prop",
+ ],
out: ["build.prop.out"],
- cmd: "cp $(in) $(out); echo ro.product.cpu.abilist=arm64-v8a >> $(out)",
+ cmd: "(echo '# build properties from buildinfo.prop module' && " +
+ "grep ro\\.build\\.version\\.codename= $(location :buildinfo.prop) && " +
+ "grep ro\\.build\\.version\\.release= $(location :buildinfo.prop) && " +
+ "grep ro\\.build\\.version\\.sdk= $(location :buildinfo.prop) && " +
+ "grep ro\\.build\\.version\\.security_patch= $(location :buildinfo.prop) && " +
+ "cat $(location build.prop) && " +
+ "echo ro.product.cpu.abilist=arm64-v8a) > $(out)",
}
android_filesystem {
diff --git a/microdroid/build.prop b/microdroid/build.prop
index 0908a4c..a9824c0 100644
--- a/microdroid/build.prop
+++ b/microdroid/build.prop
@@ -3,11 +3,5 @@
ro.adb.secure=0
service.adb.listen_addrs=vsock:5555
-# TODO(b/189164487): support build related properties
-ro.build.version.codename=UpsideDownCake
-ro.build.version.release=13
-ro.build.version.sdk=33
-ro.build.version.security_patch=2022-06-05
-
# Payload metadata partition
apexd.payload_metadata.path=/dev/block/by-name/payload-metadata
diff --git a/microdroid/payload/mk_payload.cc b/microdroid/payload/mk_payload.cc
index 6e3f526..4dbcabf 100644
--- a/microdroid/payload/mk_payload.cc
+++ b/microdroid/payload/mk_payload.cc
@@ -166,11 +166,10 @@
Metadata metadata;
metadata.set_version(1);
- int apex_index = 0;
for (const auto& apex_config : config.apexes) {
auto* apex = metadata.add_apexes();
apex->set_name(apex_config.name);
- apex->set_partition_name("microdroid-apex-" + std::to_string(apex_index++));
+ apex->set_partition_name(apex_config.name);
apex->set_is_factory(true);
}
@@ -303,4 +302,4 @@
}
return 0;
-}
\ No newline at end of file
+}
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 5dbd4ec..7385288 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -13,7 +13,7 @@
"libcore.rust_sysroot",
],
rustlibs: [
- "libspin_nostd",
+ "libvmbase",
],
enabled: false,
target: {
diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs
index 2bdcf9c..61f7846 100644
--- a/pvmfw/src/exceptions.rs
+++ b/pvmfw/src/exceptions.rs
@@ -14,61 +14,59 @@
//! Exception handlers.
-use crate::console::emergency_write_str;
-use crate::eprintln;
-use crate::psci::system_reset;
use core::arch::asm;
+use vmbase::{console::emergency_write_str, eprintln, power::reboot};
#[no_mangle]
extern "C" fn sync_exception_current() {
emergency_write_str("sync_exception_current\n");
print_esr();
- system_reset();
+ reboot();
}
#[no_mangle]
extern "C" fn irq_current() {
emergency_write_str("irq_current\n");
- system_reset();
+ reboot();
}
#[no_mangle]
extern "C" fn fiq_current() {
emergency_write_str("fiq_current\n");
- system_reset();
+ reboot();
}
#[no_mangle]
extern "C" fn serr_current() {
emergency_write_str("serr_current\n");
print_esr();
- system_reset();
+ reboot();
}
#[no_mangle]
extern "C" fn sync_lower() {
emergency_write_str("sync_lower\n");
print_esr();
- system_reset();
+ reboot();
}
#[no_mangle]
extern "C" fn irq_lower() {
emergency_write_str("irq_lower\n");
- system_reset();
+ reboot();
}
#[no_mangle]
extern "C" fn fiq_lower() {
emergency_write_str("fiq_lower\n");
- system_reset();
+ reboot();
}
#[no_mangle]
extern "C" fn serr_lower() {
emergency_write_str("serr_lower\n");
print_esr();
- system_reset();
+ reboot();
}
#[inline]
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index d38b1e3..3fe3435 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -17,13 +17,9 @@
#![no_main]
#![no_std]
-mod console;
mod exceptions;
-mod psci;
-mod uart;
-use core::panic::PanicInfo;
-use psci::{system_off, system_reset};
+use vmbase::{console, power::shutdown, println};
/// Entry point for pVM firmware.
#[no_mangle]
@@ -31,14 +27,5 @@
console::init();
println!("Hello world");
- system_off();
- #[allow(clippy::empty_loop)]
- loop {}
-}
-
-#[panic_handler]
-fn panic(info: &PanicInfo) -> ! {
- eprintln!("{}", info);
- system_reset();
- loop {}
+ shutdown();
}
diff --git a/pvmfw/src/psci.rs b/pvmfw/src/psci.rs
deleted file mode 100644
index 8dcbcaa..0000000
--- a/pvmfw/src/psci.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2022, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! PSCI calls.
-
-const PSCI_SYSTEM_OFF: u32 = 0x84000008;
-const PSCI_SYSTEM_RESET: u32 = 0x84000009;
-const PSCI_SYSTEM_RESET2: u32 = 0x84000012;
-
-pub fn system_off() -> u32 {
- hvc32(PSCI_SYSTEM_OFF, 0, 0, 0, 0, 0, 0, 0)[0]
-}
-
-pub fn system_reset() -> u32 {
- hvc32(PSCI_SYSTEM_RESET, 0, 0, 0, 0, 0, 0, 0)[0]
-}
-
-#[allow(unused)]
-pub fn system_reset2(reset_type: u32, cookie: u32) -> u32 {
- hvc32(PSCI_SYSTEM_RESET2, reset_type, cookie, 0, 0, 0, 0, 0)[0]
-}
-
-/// Make an HVC32 call to the hypervisor, following the SMC Calling Convention version 1.3.
-#[inline(always)]
-#[allow(clippy::too_many_arguments)]
-fn hvc32(
- function: u32,
- arg1: u32,
- arg2: u32,
- arg3: u32,
- arg4: u32,
- arg5: u32,
- arg6: u32,
- arg7: u32,
-) -> [u32; 8] {
- let mut ret = [0; 8];
-
- #[cfg(target_arch = "aarch64")]
- unsafe {
- core::arch::asm!(
- "hvc #0",
- inout("w0") function => ret[0],
- inout("w1") arg1 => ret[1],
- inout("w2") arg2 => ret[2],
- inout("w3") arg3 => ret[3],
- inout("w4") arg4 => ret[4],
- inout("w5") arg5 => ret[5],
- inout("w6") arg6 => ret[6],
- inout("w7") arg7 => ret[7],
- options(nomem, nostack)
- )
- }
-
- #[cfg(not(target_arch = "aarch64"))]
- unimplemented!();
-
- ret
-}
diff --git a/tests/hostside/helper/Android.bp b/tests/hostside/helper/Android.bp
index 4ca0bf0..6ab02f8 100644
--- a/tests/hostside/helper/Android.bp
+++ b/tests/hostside/helper/Android.bp
@@ -6,7 +6,8 @@
name: "VirtualizationTestHelper",
srcs: ["java/**/*.java"],
libs: [
- "tradefed",
"compatibility-tradefed",
+ "tradefed",
+ "truth-prebuilt",
],
}
diff --git a/tests/hostside/helper/java/android/virt/test/CommandResultSubject.java b/tests/hostside/helper/java/android/virt/test/CommandResultSubject.java
new file mode 100644
index 0000000..5312f5a
--- /dev/null
+++ b/tests/hostside/helper/java/android/virt/test/CommandResultSubject.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.virt.test;
+
+import static com.google.common.truth.Truth.assertAbout;
+
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.IntegerSubject;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Subject;
+
+/**
+ * A <a href="https://github.com/google/truth">Truth</a> subject for {@link CommandResult}.
+ */
+public class CommandResultSubject extends Subject {
+ private final CommandResult mActual;
+
+ public static Factory<CommandResultSubject, CommandResult> command_results() {
+ return CommandResultSubject::new;
+ }
+
+ public static CommandResultSubject assertThat(CommandResult actual) {
+ return assertAbout(command_results()).that(actual);
+ }
+
+ private CommandResultSubject(FailureMetadata metadata, CommandResult actual) {
+ super(metadata, actual);
+ this.mActual = actual;
+ }
+
+ public void isSuccess() {
+ check("isSuccess()").that(mActual.getStatus()).isEqualTo(CommandStatus.SUCCESS);
+ }
+
+ public void isFailed() {
+ check("isFailed()").that(mActual.getStatus()).isEqualTo(CommandStatus.FAILED);
+ }
+
+ public void isTimedOut() {
+ check("isTimedOut()").that(mActual.getStatus()).isEqualTo(CommandStatus.TIMED_OUT);
+ }
+
+ public void isException() {
+ check("isException()").that(mActual.getStatus()).isEqualTo(CommandStatus.EXCEPTION);
+ }
+
+ public IntegerSubject exitCode() {
+ return check("exitCode()").that(mActual.getExitCode());
+ }
+
+ public StringSubject stdoutTrimmed() {
+ return check("stdout()").that(mActual.getStdout().trim());
+ }
+
+ public StringSubject stderrTrimmed() {
+ return check("stderr()").that(mActual.getStderr().trim());
+ }
+}
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index dc0284f..a55ebe1 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -16,10 +16,13 @@
package android.virt.test;
+import static android.virt.test.CommandResultSubject.assertThat;
+import static android.virt.test.CommandResultSubject.command_results;
+
import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@@ -29,10 +32,8 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.TestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.RunUtil;
import java.io.File;
@@ -113,16 +114,17 @@
private static String runOnHostWithTimeout(long timeoutMillis, String... cmd) {
assertTrue(timeoutMillis >= 0);
CommandResult result = RunUtil.getDefault().runTimedCmd(timeoutMillis, cmd);
- assertThat(result.getStatus(), is(CommandStatus.SUCCESS));
+ assertThat(result).isSuccess();
return result.getStdout().trim();
}
// Run a shell command on Microdroid
public static String runOnMicrodroid(String... cmd) {
CommandResult result = runOnMicrodroidForResult(cmd);
- if (result.getStatus() != CommandStatus.SUCCESS) {
- fail(join(cmd) + " has failed: " + result);
- }
+ assertWithMessage("microdroid shell cmd `" + join(cmd) + "`")
+ .about(command_results())
+ .that(result)
+ .isSuccess();
return result.getStdout().trim();
}
@@ -133,23 +135,13 @@
CommandResult result = RunUtil.getDefault()
.runTimedCmdRetry(timeoutMs, 500, attempts,
"adb", "-s", MICRODROID_SERIAL, "shell", join(cmd));
- if (result.getStatus() != CommandStatus.SUCCESS) {
- fail(join(cmd) + " has failed: " + result);
- }
+ assertWithMessage("Command `" + cmd + "` has failed")
+ .about(command_results())
+ .that(result)
+ .isSuccess();
return result.getStdout().trim();
}
- // Same as runOnMicrodroid, but returns null on error.
- public static String tryRunOnMicrodroid(String... cmd) {
- CommandResult result = runOnMicrodroidForResult(cmd);
- if (result.getStatus() == CommandStatus.SUCCESS) {
- return result.getStdout().trim();
- } else {
- CLog.d(join(cmd) + " has failed (but ok): " + result);
- return null;
- }
- }
-
public static CommandResult runOnMicrodroidForResult(String... cmd) {
final long timeoutMs = 30000; // 30 sec. Microdroid is extremely slow on GCE-on-CF.
return RunUtil.getDefault()
@@ -168,15 +160,15 @@
"pull",
path,
target.getPath());
- if (result.getStatus() != CommandStatus.SUCCESS) {
- fail("pulling " + path + " has failed: " + result);
- }
+ assertWithMessage("pulling " + path + " from microdroid")
+ .about(command_results())
+ .that(result)
+ .isSuccess();
}
// Asserts the command will fail on Microdroid.
public static void assertFailedOnMicrodroid(String... cmd) {
- CommandResult result = runOnMicrodroidForResult(cmd);
- assertThat(result.getStatus(), is(CommandStatus.FAILED));
+ assertThat(runOnMicrodroidForResult(cmd)).isFailed();
}
private static String join(String... strs) {
@@ -390,6 +382,8 @@
}
// Check if it actually booted by reading a sysprop.
- assertThat(runOnMicrodroid("getprop", "ro.hardware"), is("microdroid"));
+ assertThat(runOnMicrodroidForResult("getprop", "ro.hardware"))
+ .stdoutTrimmed()
+ .isEqualTo("microdroid");
}
}
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 32bdf3b..6a5689d 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -16,11 +16,16 @@
package android.virt.test;
+import static android.virt.test.CommandResultSubject.assertThat;
+import static android.virt.test.CommandResultSubject.command_results;
+
import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
@@ -34,7 +39,6 @@
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.RunUtil;
@@ -105,7 +109,7 @@
// the microdroid boot procedure. Therefore, waiting for the service means that we wait for
// the boot to complete. TODO: we need a better marker eventually.
private void waitForLogdInit() {
- tryRunOnMicrodroid("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
+ runOnMicrodroidForResult("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
}
@Test
@@ -122,7 +126,7 @@
assertFalse(runDeviceTests(options));
Map<TestDescription, TestResult> results = getLastDeviceRunResults().getTestResults();
- assertThat(results.size(), is(1));
+ assertThat(results).hasSize(1);
TestResult result = results.values().toArray(new TestResult[0])[0];
assertTrue("The test should fail with a permission error",
result.getStackTrace()
@@ -161,9 +165,11 @@
String.join(" ", command));
String out = result.getStdout();
String err = result.getStderr();
- assertEquals(
- "resigning the Virt APEX failed:\n\tout: " + out + "\n\terr: " + err + "\n",
- CommandStatus.SUCCESS, result.getStatus());
+ assertWithMessage(
+ "resigning the Virt APEX failed:\n\tout: " + out + "\n\terr: " + err + "\n")
+ .about(command_results())
+ .that(result)
+ .isSuccess();
}
private static <T> void assertThatEventually(long timeoutMillis, Callable<T> callable,
@@ -263,8 +269,8 @@
// - apk and idsig
disks.put(new JSONObject().put("writable", false).put("partitions", new JSONArray()
.put(newPartition("payload-metadata", payloadMetadataPath))
- .put(newPartition("microdroid-apex-0", statsdApexPath))
- .put(newPartition("microdroid-apex-1", adbdApexPath))
+ .put(newPartition("com.android.os.statsd", statsdApexPath))
+ .put(newPartition("com.android.adbd", adbdApexPath))
.put(newPartition("microdroid-apk", apkPath))
.put(newPartition("microdroid-apk-idsig", idSigPath))));
@@ -458,10 +464,10 @@
"-w",
"-f",
generalPolicyConfFile.getPath());
- assertEquals(
- "neverallow check failed: " + result.getStderr().trim(),
- result.getStatus(),
- CommandStatus.SUCCESS);
+ assertWithMessage("neverallow check failed: " + result.getStderr().trim())
+ .about(command_results())
+ .that(result)
+ .isSuccess();
}
shutdownMicrodroid(getDevice(), cid);
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
new file mode 100644
index 0000000..972cd1b
--- /dev/null
+++ b/vmbase/Android.bp
@@ -0,0 +1,18 @@
+rust_library_rlib {
+ name: "libvmbase",
+ host_supported: false,
+ crate_name: "vmbase",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libpsci",
+ "libspin_nostd",
+ ],
+ enabled: false,
+ target: {
+ android_arm64: {
+ enabled: true,
+ },
+ },
+ apex_available: ["com.android.virt"],
+}
diff --git a/pvmfw/src/console.rs b/vmbase/src/console.rs
similarity index 100%
rename from pvmfw/src/console.rs
rename to vmbase/src/console.rs
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
new file mode 100644
index 0000000..0901e04
--- /dev/null
+++ b/vmbase/src/lib.rs
@@ -0,0 +1,30 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Basic functionality for bare-metal binaries to run in a VM under crosvm.
+
+#![no_std]
+
+pub mod console;
+pub mod power;
+pub mod uart;
+
+use core::panic::PanicInfo;
+use power::reboot;
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ eprintln!("{}", info);
+ reboot()
+}
diff --git a/vmbase/src/power.rs b/vmbase/src/power.rs
new file mode 100644
index 0000000..10a5e5d
--- /dev/null
+++ b/vmbase/src/power.rs
@@ -0,0 +1,35 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Functions for shutting down the VM.
+
+use psci::{system_off, system_reset};
+
+/// Makes a `PSCI_SYSTEM_OFF` call to shutdown the VM.
+///
+/// Panics if it returns an error.
+pub fn shutdown() -> ! {
+ system_off().unwrap();
+ #[allow(clippy::empty_loop)]
+ loop {}
+}
+
+/// Makes a `PSCI_SYSTEM_RESET` call to shutdown the VM abnormally.
+///
+/// Panics if it returns an error.
+pub fn reboot() -> ! {
+ system_reset().unwrap();
+ #[allow(clippy::empty_loop)]
+ loop {}
+}
diff --git a/pvmfw/src/uart.rs b/vmbase/src/uart.rs
similarity index 100%
rename from pvmfw/src/uart.rs
rename to vmbase/src/uart.rs