Merge "Maximize AuthFS's metadata timeout"
diff --git a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
index a8abf97..0f60384 100644
--- a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
+++ b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "8149267"
+ build_id: "8160972"
target: "u-boot_pvmfw"
source_file: "pvmfw.img"
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 6338c82..7be81a8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -7,7 +7,7 @@
"name": "ComposHostTestCases"
},
{
- "name": "VirtualizationTestCases"
+ "name": "VirtualizationTestCases.64"
},
{
"name": "MicrodroidTestApp"
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 8fe3403..207c938 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -15,7 +15,8 @@
# limitations under the License.
"""sign_virt_apex is a command line tool for sign the Virt APEX file.
-Typical usage: sign_virt_apex [-v] [--avbtool path_to_avbtool] path_to_key payload_contents_dir
+Typical usage:
+ sign_virt_apex [-v] [--avbtool path_to_avbtool] [--signing_args args] payload_key payload_dir
sign_virt_apex uses external tools which are assumed to be available via PATH.
- avbtool (--avbtool can override the tool)
@@ -26,6 +27,7 @@
import hashlib
import os
import re
+import shlex
import shutil
import subprocess
import sys
@@ -45,6 +47,10 @@
default='avbtool',
help='Optional flag that specifies the AVB tool to use. Defaults to `avbtool`.')
parser.add_argument(
+ '--signing_args',
+ help='the extra signing arguments passed to avbtool.'
+ )
+ parser.add_argument(
'key',
help='path to the private key file.')
parser.add_argument(
@@ -163,6 +169,8 @@
'--partition_name', partition_name,
'--partition_size', partition_size,
'--image', image_path]
+ if args.signing_args:
+ cmd.extend(shlex.split(args.signing_args))
RunCommand(args, cmd)
@@ -182,6 +190,8 @@
'--partition_size', partition_size,
'--do_not_generate_fec',
'--image', image_path]
+ if args.signing_args:
+ cmd.extend(shlex.split(args.signing_args))
RunCommand(args, cmd)
@@ -216,6 +226,9 @@
cmd.extend(['--chain_partition', '%s:%s:%s' %
(part_name, ril, avbpubkey)])
+ if args.signing_args:
+ cmd.extend(shlex.split(args.signing_args))
+
RunCommand(args, cmd)
# libavb expects to be able to read the maximum vbmeta size, so we must provide a partition
# which matches this or the read will fail.
diff --git a/authfs/tests/Android.bp b/authfs/tests/Android.bp
index 30606e5..1b5cf09 100644
--- a/authfs/tests/Android.bp
+++ b/authfs/tests/Android.bp
@@ -15,6 +15,7 @@
],
test_suites: ["general-tests"],
data_device_bins: ["open_then_run"],
+ per_testcase_directory: true,
data: [
":authfs_test_files",
":MicrodroidTestApp",
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 69f095a..b754ba7 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -206,8 +206,7 @@
return Ok(true);
}
- let build_type = system_properties::read("ro.build.type")?.context("ro.build.type not set")?;
- let is_debug_build = matches!(build_type.as_str(), "userdebug" | "eng");
+ let is_debug_build = system_properties::read("ro.debuggable")?.as_deref().unwrap_or("0") == "1";
if !is_debug_build {
bail!("Protected VM not supported, unable to start VM");
}
@@ -215,7 +214,7 @@
let have_unprotected_vm =
system_properties::read_bool("ro.boot.hypervisor.vm.supported", false)?;
if have_unprotected_vm {
- warn!("Protected VM not supported, falling back to unprotected on {} build", build_type);
+ warn!("Protected VM not supported, falling back to unprotected on debuggable build");
return Ok(false);
}
diff --git a/compos/service/java/com/android/server/compos/IsolatedCompilationService.java b/compos/service/java/com/android/server/compos/IsolatedCompilationService.java
index bd272a0..11e3743 100644
--- a/compos/service/java/com/android/server/compos/IsolatedCompilationService.java
+++ b/compos/service/java/com/android/server/compos/IsolatedCompilationService.java
@@ -16,6 +16,8 @@
package com.android.server.compos;
+import static android.os.Build.isDebuggable;
+
import android.annotation.NonNull;
import android.app.job.JobScheduler;
import android.content.Context;
@@ -25,12 +27,11 @@
import android.content.pm.StagedApexInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.sysprop.HypervisorProperties;
import android.util.Log;
import com.android.server.SystemService;
-import java.io.File;
-
/**
* A system service responsible for performing Isolated Compilation (compiling boot & system server
* classpath JARs in a protected VM) when appropriate.
@@ -71,12 +72,20 @@
}
private static boolean isIsolatedCompilationSupported() {
- // Check that KVM is enabled on the device
- if (!new File("/dev/kvm").exists()) {
- return false;
+ // The CompOS APEX is present or we wouldn't be here. So just check that the device
+ // has a suitably capable hypervisor.
+
+ // We really want a protected VM
+ if (HypervisorProperties.hypervisor_protected_vm_supported().orElse(false)) {
+ return true;
}
- return true;
+ // But can use a non-protected VM on a debug build
+ if (isDebuggable()) {
+ return HypervisorProperties.hypervisor_vm_supported().orElse(false);
+ }
+
+ return false;
}
private static class StagedApexObserver extends IStagedApexObserver.Stub {
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index ae4a29d..6049991 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -22,12 +22,9 @@
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
-use std::fs::read_dir;
use std::path::{self, Path, PathBuf};
use std::process::Command;
-use crate::artifact_signer::ArtifactSigner;
-use crate::signing_key::DiceSigner;
use authfs_aidl_interface::aidl::com::android::virt::fs::{
AuthFsConfig::{
AuthFsConfig, InputDirFdAnnotation::InputDirFdAnnotation,
@@ -105,12 +102,15 @@
system_properties::read_bool(name, false).unwrap_or(false)
}
-pub fn odrefresh(
+pub fn odrefresh<F>(
odrefresh_path: &Path,
context: OdrefreshContext,
authfs_service: Strong<dyn IAuthFsService>,
- signer: DiceSigner,
-) -> Result<ExitCode> {
+ success_fn: F,
+) -> Result<ExitCode>
+where
+ F: FnOnce(PathBuf) -> Result<()>,
+{
// Mount authfs (via authfs_service). The authfs instance unmounts once the `authfs` variable
// is out of scope.
let authfs_config = AuthFsConfig {
@@ -183,13 +183,8 @@
info!("odrefresh exited with {:?}", exit_code);
if exit_code == ExitCode::CompilationSuccess {
- // authfs only shows us the files we created, so it's ok to just sign everything under
- // the target directory.
let target_dir = art_apex_data.join(context.target_dir_name);
- let mut artifact_signer = ArtifactSigner::new(&target_dir);
- add_artifacts(&target_dir, &mut artifact_signer)?;
-
- artifact_signer.write_info_and_signature(signer, &target_dir.join("compos.info"))?;
+ success_fn(target_dir)?;
}
Ok(exit_code)
@@ -245,24 +240,6 @@
Ok(())
}
-fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
- for entry in
- read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
- {
- let entry = entry?;
- let file_type = entry.file_type()?;
- if file_type.is_dir() {
- add_artifacts(&entry.path(), artifact_signer)?;
- } else if file_type.is_file() {
- artifact_signer.add_artifact(&entry.path())?;
- } else {
- // authfs shouldn't create anything else, but just in case
- bail!("Unexpected file type in artifacts: {:?}", entry);
- }
- }
- Ok(())
-}
-
fn spawn_jailed_task(executable: &Path, args: &[String], env_vars: &[String]) -> Result<Minijail> {
// TODO(b/185175567): Run in a more restricted sandbox.
let jail = Minijail::new()?;
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 3ec15dd..5f3ee62 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -18,17 +18,19 @@
//! file descriptors backed by authfs (via authfs_service) and pass the file descriptors to the
//! actual compiler.
-use anyhow::{Context, Result};
+use anyhow::{bail, Context, Result};
use binder_common::new_binder_exception;
use compos_common::binder::to_binder_result;
use log::warn;
use std::default::Default;
-use std::path::PathBuf;
+use std::fs::read_dir;
+use std::path::{Path, PathBuf};
use std::sync::RwLock;
+use crate::artifact_signer::ArtifactSigner;
use crate::compilation::{odrefresh, OdrefreshContext};
use crate::dice::Dice;
-use crate::signing_key::{DiceSigner, DiceSigningKey};
+use crate::signing_key::DiceSigningKey;
use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFsService::IAuthFsService;
use compos_aidl_interface::aidl::com::android::compos::{
CompOsKeyData::CompOsKeyData,
@@ -57,17 +59,6 @@
key_blob: RwLock<Vec<u8>>,
}
-impl CompOsService {
- fn new_signer(&self) -> BinderResult<DiceSigner> {
- let key = &*self.key_blob.read().unwrap();
- if key.is_empty() {
- Err(new_binder_exception(ExceptionCode::ILLEGAL_STATE, "Key is not initialized"))
- } else {
- to_binder_result(self.signing_key.new_signer(key))
- }
- }
-}
-
impl Interface for CompOsService {}
impl ICompOsService for CompOsService {
@@ -91,6 +82,14 @@
zygote_arch: &str,
system_server_compiler_filter: &str,
) -> BinderResult<i8> {
+ let key = &*self.key_blob.read().unwrap();
+ if key.is_empty() {
+ return Err(new_binder_exception(
+ ExceptionCode::ILLEGAL_STATE,
+ "Key is not initialized",
+ ));
+ }
+
let context = to_binder_result(OdrefreshContext::new(
compilation_mode,
system_dir_fd,
@@ -103,8 +102,16 @@
let authfs_service = get_authfs_service()?;
let exit_code = to_binder_result(
- odrefresh(&self.odrefresh_path, context, authfs_service, self.new_signer()?)
- .context("odrefresh failed"),
+ odrefresh(&self.odrefresh_path, context, authfs_service, |output_dir| {
+ // authfs only shows us the files we created, so it's ok to just sign everything
+ // under the output directory.
+ let mut artifact_signer = ArtifactSigner::new(&output_dir);
+ add_artifacts(&output_dir, &mut artifact_signer)?;
+
+ let signer = to_binder_result(self.signing_key.new_signer(key))?;
+ artifact_signer.write_info_and_signature(signer, &output_dir.join("compos.info"))
+ })
+ .context("odrefresh failed"),
)?;
Ok(exit_code as i8)
}
@@ -126,3 +133,21 @@
fn get_authfs_service() -> BinderResult<Strong<dyn IAuthFsService>> {
Ok(authfs_aidl_interface::binder::get_interface(AUTHFS_SERVICE_NAME)?)
}
+
+fn add_artifacts(target_dir: &Path, artifact_signer: &mut ArtifactSigner) -> Result<()> {
+ for entry in
+ read_dir(&target_dir).with_context(|| format!("Traversing {}", target_dir.display()))?
+ {
+ let entry = entry?;
+ let file_type = entry.file_type()?;
+ if file_type.is_dir() {
+ add_artifacts(&entry.path(), artifact_signer)?;
+ } else if file_type.is_file() {
+ artifact_signer.add_artifact(&entry.path())?;
+ } else {
+ // authfs shouldn't create anything else, but just in case
+ bail!("Unexpected file type in artifacts: {:?}", entry);
+ }
+ }
+ Ok(())
+}
diff --git a/compos/tests/Android.bp b/compos/tests/Android.bp
index d380059..c178ddd 100644
--- a/compos/tests/Android.bp
+++ b/compos/tests/Android.bp
@@ -13,5 +13,7 @@
static_libs: [
"VirtualizationTestHelper",
],
- test_suites: ["general-tests"],
+ test_suites: [
+ "general-tests",
+ ],
}
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 6d3b208..ac5d38b 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -36,7 +36,7 @@
all can run via `atest`.
```shell
-atest VirtualizationTestCases
+atest VirtualizationTestCases.64
atest MicrodroidHostTestCases
atest MicrodroidTestApp
```
diff --git a/microdroid/payload/mk_payload.cc b/microdroid/payload/mk_payload.cc
index 33e91b9..fd1ce78 100644
--- a/microdroid/payload/mk_payload.cc
+++ b/microdroid/payload/mk_payload.cc
@@ -171,6 +171,7 @@
auto* apex = metadata.add_apexes();
apex->set_name(apex_config.name);
apex->set_partition_name("microdroid-apex-" + std::to_string(apex_index++));
+ apex->set_is_factory(true);
}
if (config.apk.has_value()) {
diff --git a/pvmfw/pvmfw.img b/pvmfw/pvmfw.img
index c036c76..27c2d2b 100644
--- a/pvmfw/pvmfw.img
+++ b/pvmfw/pvmfw.img
Binary files differ
diff --git a/tests/Android.bp b/tests/Android.bp
index 35ff6a0..74d58f5 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -21,8 +21,13 @@
kernel_stem = "kernel_prebuilts-" + kernel_version
cc_test {
- name: "VirtualizationTestCases",
- test_suites: ["general-tests"],
+ // ".64" suffix is to work around cts-unit-test which is demanding that all
+ // executables in CTS should have both 32 and 64 ABIs.
+ name: "VirtualizationTestCases.64",
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
srcs: [
"common.cc",
"vsock_test.cc",
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index 5e7faf9..68e9c1b 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -15,11 +15,16 @@
-->
<configuration description="Config for Virtualization tests">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="security" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<!-- Push test binaries to the device. -->
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="abort-on-push-failure" value="true" />
- <option name="push-file" key="VirtualizationTestCases" value="/data/local/tmp/virt-test/VirtualizationTestCases" />
+ <option name="push-file" key="VirtualizationTestCases.64" value="/data/local/tmp/virt-test/VirtualizationTestCases.64" />
<option name="push-file" key="virt_test_kernel" value="/data/local/tmp/virt-test/kernel" />
<option name="push-file" key="virt_test_initramfs.img" value="/data/local/tmp/virt-test/initramfs" />
</target_preparer>
@@ -30,7 +35,7 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp/virt-test" />
- <option name="module-name" value="VirtualizationTestCases" />
+ <option name="module-name" value="VirtualizationTestCases.64" />
<!-- test-timeout unit is ms, value = 2 minutes -->
<option name="native-test-timeout" value="120000" />
</test>
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 1aef796..bc8a4a5 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -5,12 +5,16 @@
java_test_host {
name: "MicrodroidHostTestCases",
srcs: ["java/**/*.java"],
- test_suites: ["general-tests"],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
libs: [
"tradefed",
],
static_libs: [
"VirtualizationTestHelper",
],
+ per_testcase_directory: true,
data: [":MicrodroidTestApp"],
}
diff --git a/tests/hostside/AndroidTest.xml b/tests/hostside/AndroidTest.xml
index e8aced6..79428ce 100644
--- a/tests/hostside/AndroidTest.xml
+++ b/tests/hostside/AndroidTest.xml
@@ -14,6 +14,11 @@
limitations under the License.
-->
<configuration description="Tests for microdroid">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="security" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="MicrodroidHostTestCases.jar" />
</test>
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index a8b068c..0699e3d 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -4,7 +4,10 @@
android_test {
name: "MicrodroidTestApp",
- test_suites: ["general-tests"],
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
srcs: ["src/java/**/*.java"],
static_libs: [
"androidx.test.runner",
@@ -16,6 +19,8 @@
jni_libs: ["MicrodroidTestNativeLib"],
platform_apis: true,
use_embedded_native_libs: true,
+ // We only support 64-bit ABI, but CTS demands all APKs to be multi-ABI.
+ compile_multilib: "both",
}
// TODO(jiyong): make this a binary, not a shared library
diff --git a/tests/testapk/AndroidTest.xml b/tests/testapk/AndroidTest.xml
index c7097db..e8bb1aa 100644
--- a/tests/testapk/AndroidTest.xml
+++ b/tests/testapk/AndroidTest.xml
@@ -14,7 +14,12 @@
limitations under the License.
-->
<configuration description="Runs sample instrumentation test.">
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="security" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="test-file-name" value="MicrodroidTestApp.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 8cfb3a1..0587299 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -30,6 +30,7 @@
import android.os.Build;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineCallback;
import android.system.virtualmachine.VirtualMachineConfig;
@@ -51,6 +52,7 @@
import java.io.File;
import java.io.IOException;
+import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -62,6 +64,8 @@
public class MicrodroidTests {
@Rule public Timeout globalTimeout = Timeout.seconds(300);
+ private static final String KERNEL_VERSION = SystemProperties.get("ro.kernel.version");
+
private static class Inner {
public Context mContext;
public VirtualMachineManager mVmm;
@@ -140,6 +144,11 @@
@Test
public void connectToVmService() throws VirtualMachineException, InterruptedException {
+ assume()
+ .withMessage("SKip on 5.4 kernel. b/218303240")
+ .that(KERNEL_VERSION)
+ .isNotEqualTo("5.4");
+
VirtualMachineConfig.Builder builder =
new VirtualMachineConfig.Builder(mInner.mContext,
"assets/vm_config_extra_apk.json");
@@ -218,6 +227,11 @@
.that(android.os.Build.DEVICE)
.isNotEqualTo("vsoc_x86_64");
+ assume()
+ .withMessage("SKip on 5.4 kernel. b/218303240")
+ .that(KERNEL_VERSION)
+ .isNotEqualTo("5.4");
+
VirtualMachineConfig.Builder builder =
new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
@@ -301,6 +315,11 @@
.that(android.os.Build.DEVICE)
.isNotEqualTo("vsoc_x86_64");
+ assume()
+ .withMessage("SKip on 5.4 kernel. b/218303240")
+ .that(KERNEL_VERSION)
+ .isNotEqualTo("5.4");
+
byte[] vm_a_secret = launchVmAndGetSecret("test_vm_a");
byte[] vm_b_secret = launchVmAndGetSecret("test_vm_b");
assertThat(vm_a_secret).isNotNull();
@@ -316,10 +335,93 @@
.that(android.os.Build.DEVICE)
.isNotEqualTo("vsoc_x86_64");
+ assume()
+ .withMessage("SKip on 5.4 kernel. b/218303240")
+ .that(KERNEL_VERSION)
+ .isNotEqualTo("5.4");
+
byte[] vm_secret_first_boot = launchVmAndGetSecret("test_vm");
byte[] vm_secret_second_boot = launchVmAndGetSecret("test_vm");
assertThat(vm_secret_first_boot).isNotNull();
assertThat(vm_secret_second_boot).isNotNull();
assertThat(vm_secret_first_boot).isEqualTo(vm_secret_second_boot);
}
+
+ @Test
+ public void bootFailsWhenInstanceDiskIsCompromised()
+ throws VirtualMachineException, InterruptedException, IOException {
+ assume().withMessage("Skip on Cuttlefish. b/195765441")
+ .that(android.os.Build.DEVICE)
+ .isNotEqualTo("vsoc_x86_64");
+
+ VirtualMachineConfig config =
+ new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json")
+ .debugLevel(DebugLevel.NONE)
+ .build();
+
+ // Remove any existing VM so we can start from scratch
+ VirtualMachine oldVm = mInner.mVmm.getOrCreate("test_vm_integrity", config);
+ oldVm.delete();
+
+ mInner.mVm = mInner.mVmm.getOrCreate("test_vm_integrity", config);
+
+ VmEventListener listener =
+ new VmEventListener() {
+ private boolean mPayloadReadyCalled = false;
+
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {
+ mPayloadReadyCalled = true;
+ forceStop(vm);
+ }
+
+ @Override
+ public void onDied(VirtualMachine vm, @DeathReason int reason) {
+ assertTrue(mPayloadReadyCalled);
+ }
+ };
+ listener.runToFinish(mInner.mVm);
+
+ // Launch the same VM after flipping a bit of the instance image.
+ // Flip actual data, as flipping trivial bits like the magic string isn't interesting.
+ File vmRoot = new File(mInner.mContext.getFilesDir(), "vm");
+ File vmDir = new File(vmRoot, "test_vm_integrity");
+ File instanceImgPath = new File(vmDir, "instance.img");
+ RandomAccessFile instanceFile = new RandomAccessFile(instanceImgPath, "rw");
+
+ // microdroid data partition starts at 0x60200, actual data at 0x60400, based on experiment
+ // TODO: parse image file (QEMU qcow2) correctly?
+ long headerOffset = 0x60400;
+ instanceFile.seek(headerOffset);
+ int b = instanceFile.readByte();
+ instanceFile.seek(headerOffset);
+ instanceFile.writeByte(b ^ 1);
+ instanceFile.close();
+
+ mInner.mVm = mInner.mVmm.get("test_vm_integrity"); // re-load the vm with new instance disk
+ listener =
+ new VmEventListener() {
+ private boolean mPayloadStarted = false;
+ private boolean mErrorOccurred = false;
+
+ @Override
+ public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
+ mPayloadStarted = true;
+ forceStop(vm);
+ }
+
+ @Override
+ public void onError(VirtualMachine vm, int errorCode, String message) {
+ mErrorOccurred = true;
+ forceStop(vm);
+ }
+
+ @Override
+ public void onDied(VirtualMachine vm, @DeathReason int reason) {
+ assertFalse(mPayloadStarted);
+ assertTrue(mErrorOccurred);
+ }
+ };
+ listener.runToFinish(mInner.mVm);
+ }
}
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 2c50fed..df7a7d2 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -272,6 +272,13 @@
if config.protected {
command.arg("--protected-vm");
+
+ // 3 virtio-console devices + vsock = 4.
+ let virtio_pci_device_count = 4 + config.disks.len();
+ // crosvm virtio queue has 256 entries, so 2 MiB per device (2 pages per entry) should be
+ // enough.
+ let swiotlb_size_mib = 2 * virtio_pci_device_count;
+ command.arg("--swiotlb").arg(swiotlb_size_mib.to_string());
}
if let Some(memory_mib) = config.memory_mib {