Merge "sed setMaxOutgoing{Threads,Connections}"
diff --git a/authfs/src/file/dir.rs b/authfs/src/file/dir.rs
index f3cc6f8..5d2ec9f 100644
--- a/authfs/src/file/dir.rs
+++ b/authfs/src/file/dir.rs
@@ -28,7 +28,7 @@
use crate::fsverity::VerifiedFileEditor;
use crate::fusefs::{AuthFsDirEntry, Inode};
-const MAX_ENTRIES: u16 = 100; // Arbitrary limit
+const MAX_ENTRIES: u16 = 1000; // Arbitrary limit
struct InodeInfo {
inode: Inode,
diff --git a/authfs/tests/benchmarks/Android.bp b/authfs/tests/benchmarks/Android.bp
index 9bdef7b..38ece79 100644
--- a/authfs/tests/benchmarks/Android.bp
+++ b/authfs/tests/benchmarks/Android.bp
@@ -23,7 +23,6 @@
":authfs_test_files",
":CtsApkVerityTestPrebuiltFiles",
":MicrodroidTestApp",
- ":measure_io",
],
}
@@ -36,3 +35,19 @@
"libbase",
],
}
+
+// Package measure_io binary into a jar, to bundle with the MicrodroidTestApp.
+// When MicrodroidTestApp is mounted inside the Microdroid, the zipfuse will
+// add the +x permission on it.
+java_genrule {
+ name: "measure_io_as_jar",
+ out: ["measure_io.jar"],
+ srcs: [
+ ":measure_io",
+ ],
+ cmd: "out_dir=$$(dirname $(out))" +
+ "&& bin_dir=\"bin\" " +
+ "&& mkdir -p $$out_dir/$$bin_dir" +
+ "&& cp $(in) $$out_dir/$$bin_dir" +
+ "&& jar cf $(out) -C $$out_dir $$bin_dir",
+}
diff --git a/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
index 32eafb8..085d06e 100644
--- a/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
+++ b/authfs/tests/benchmarks/src/java/com/android/fs/benchmarks/AuthFsBenchmarks.java
@@ -18,7 +18,6 @@
import static com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
-import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -45,7 +44,6 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
-import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -57,11 +55,8 @@
public class AuthFsBenchmarks extends BaseHostJUnit4Test {
private static final int TRIAL_COUNT = 5;
- /** Name of the measure_io binary on host. */
- private static final String MEASURE_IO_BIN_NAME = "measure_io";
-
/** Path to measure_io on Microdroid. */
- private static final String MEASURE_IO_BIN_PATH = "/data/local/tmp/measure_io";
+ private static final String MEASURE_IO_BIN_PATH = "/mnt/apk/bin/measure_io";
/** fs-verity digest (sha256) of testdata/input.4m */
private static final String DIGEST_4M =
@@ -123,7 +118,6 @@
}
private void readRemoteFile(String mode) throws DeviceNotAvailableException {
- pushMeasureIoBinToMicrodroid();
// Cache the file in memory for the host.
mAuthFsTestRule
.getAndroid()
@@ -146,7 +140,6 @@
}
private void writeRemoteFile(String mode) throws DeviceNotAvailableException {
- pushMeasureIoBinToMicrodroid();
String filePath = mAuthFsTestRule.MOUNT_DIR + "/5";
int fileSizeMb = 8;
String cmd = MEASURE_IO_BIN_PATH + " " + filePath + " " + fileSizeMb + " " + mode + " w";
@@ -165,14 +158,6 @@
reportMetrics(rates, mode + "_write", "mb_per_sec");
}
- private void pushMeasureIoBinToMicrodroid() throws DeviceNotAvailableException {
- File measureReadBin = mAuthFsTestRule.findTestFile(getBuild(), MEASURE_IO_BIN_NAME);
- assertThat(measureReadBin.exists()).isTrue();
- mAuthFsTestRule.getMicrodroidDevice().pushFile(measureReadBin, MEASURE_IO_BIN_PATH);
- assertThat(mAuthFsTestRule.getMicrodroid().run("ls " + MEASURE_IO_BIN_PATH))
- .isEqualTo(MEASURE_IO_BIN_PATH);
- }
-
private void reportMetrics(List<Double> metrics, String name, String unit) {
Map<String, Double> stats = mMetricsProcessor.computeStats(metrics, name, unit);
for (Map.Entry<String, Double> entry : stats.entrySet()) {
diff --git a/compos/aidl/com/android/compos/ICompOsService.aidl b/compos/aidl/com/android/compos/ICompOsService.aidl
index df8c91e..497c35e 100644
--- a/compos/aidl/com/android/compos/ICompOsService.aidl
+++ b/compos/aidl/com/android/compos/ICompOsService.aidl
@@ -87,7 +87,7 @@
/**
* Returns the attestation certificate chain of the current VM. The result is in the form of a
* CBOR encoded Boot Certificate Chain (BCC) as defined in
- * hardware/interfaces/security/dice/aidl/android/hardware/security/dice/Bcc.aidl.
+ * hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
*/
byte[] getAttestationChain();
diff --git a/compos/common/Android.bp b/compos/common/Android.bp
index 35947d7..05bc093 100644
--- a/compos/common/Android.bp
+++ b/compos/common/Android.bp
@@ -12,6 +12,7 @@
"compos_aidl_interface-rust",
"libanyhow",
"libbinder_rs",
+ "libglob",
"liblazy_static",
"liblog_rust",
"libnested_virt",
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 92c9a3c..96c8147 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -27,12 +27,13 @@
VirtualMachineAppConfig::{DebugLevel::DebugLevel, Payload::Payload, VirtualMachineAppConfig},
VirtualMachineConfig::VirtualMachineConfig,
};
-use anyhow::{bail, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
use binder::{ParcelFileDescriptor, Strong};
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use glob::glob;
use log::{info, warn};
use rustutils::system_properties;
-use std::fs::{self, File};
+use std::fs::File;
use std::path::{Path, PathBuf};
use vmclient::{DeathReason, ErrorCode, VmInstance, VmWaitError};
@@ -194,15 +195,19 @@
// Our config APK will be in a directory under app, but the name of the directory is at the
// discretion of the build system. So just look in each sub-directory until we find it.
// (In practice there will be exactly one directory, so this shouldn't take long.)
- let app_dir = apex_dir.join("app");
- for dir in fs::read_dir(app_dir).context("Reading app dir")? {
- let apk_file = dir?.path().join("CompOSPayloadApp.apk");
- if apk_file.is_file() {
- return Ok(apk_file);
- }
+ let app_glob = apex_dir.join("app").join("**").join("CompOSPayloadApp*.apk");
+ let mut entries: Vec<PathBuf> =
+ glob(app_glob.to_str().ok_or_else(|| anyhow!("Invalid path: {}", app_glob.display()))?)
+ .context("failed to glob")?
+ .filter_map(|e| e.ok())
+ .collect();
+ if entries.len() > 1 {
+ bail!("Found more than one apk matching {}", app_glob.display());
}
-
- bail!("Failed to locate CompOSPayloadApp.apk")
+ match entries.pop() {
+ Some(path) => Ok(path),
+ None => Err(anyhow!("No apks match {}", app_glob.display())),
+ }
}
fn prepare_idsig(
diff --git a/docs/debug/ramdump.md b/docs/debug/ramdump.md
index 771c608..020f054 100644
--- a/docs/debug/ramdump.md
+++ b/docs/debug/ramdump.md
@@ -73,8 +73,8 @@
Download the source code and build it as follows. This needs to be done only once.
```shell
-$ wget https://github.com/crash-utility/crash/archive/refs/tags/8.0.1.tar.gz -O - | tar xzvf
-$ make -C crash-8.0.1 target=ARM64
+$ wget https://github.com/crash-utility/crash/archive/refs/tags/8.0.2.tar.gz -O - | tar xzv
+$ make -j -C crash-8.0.2 target=ARM64
```
### Obtaining vmlinux
@@ -101,7 +101,7 @@
### Running crash(8) with the RAM dump and the kernel image
```shell
-$ crash-8.0.1/crash ramdump vmlinux
+$ crash-8.0.2/crash ramdump vmlinux
```
You can now analyze the RAM dump using the various commands that crash(8) provides. For example, `bt <pid>` command shows the stack trace of a process.
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index a6b3ed6..5f39b1c 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -1033,11 +1033,13 @@
}
checkStopped();
- // Delete any existing file before recreating; that ensures any VirtualMachineDescriptor
- // that refers to the old file does not see the new config.
- mConfigFilePath.delete();
- newConfig.serialize(mConfigFilePath);
- mConfig = newConfig;
+ if (oldConfig != newConfig) {
+ // Delete any existing file before recreating; that ensures any
+ // VirtualMachineDescriptor that refers to the old file does not see the new config.
+ mConfigFilePath.delete();
+ newConfig.serialize(mConfigFilePath);
+ mConfig = newConfig;
+ }
return oldConfig;
}
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index c364b42..93e65db 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -31,11 +31,13 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.sysprop.HypervisorProperties;
import android.system.virtualizationservice.VirtualMachineAppConfig;
import android.system.virtualizationservice.VirtualMachinePayloadConfig;
+import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
@@ -47,6 +49,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.zip.ZipFile;
/**
* Represents a configuration of a virtual machine. A configuration consists of hardware
@@ -57,6 +60,7 @@
*/
@SystemApi
public final class VirtualMachineConfig {
+ private static final String TAG = "VirtualMachineConfig";
private static final String[] EMPTY_STRING_ARRAY = {};
// These define the schema of the config file persisted on disk.
@@ -296,8 +300,7 @@
/**
* Returns the absolute path of the APK which should contain the binary payload that will
- * execute within the VM. Returns null if no specific path has been set, so the primary APK will
- * be used.
+ * execute within the VM. Returns null if no specific path has been set.
*
* @hide
*/
@@ -421,6 +424,9 @@
*/
@SystemApi
public boolean isCompatibleWith(@NonNull VirtualMachineConfig other) {
+ if (this == other) {
+ return true;
+ }
return this.mDebugLevel == other.mDebugLevel
&& this.mProtectedVm == other.mProtectedVm
&& this.mEncryptedStorageBytes == other.mEncryptedStorageBytes
@@ -442,18 +448,7 @@
throws VirtualMachineException {
VirtualMachineAppConfig vsConfig = new VirtualMachineAppConfig();
- String apkPath = mApkPath;
- if (apkPath == null) {
- try {
- ApplicationInfo appInfo =
- packageManager.getApplicationInfo(
- mPackageName, PackageManager.ApplicationInfoFlags.of(0));
- // This really is the path to the APK, not a directory.
- apkPath = appInfo.sourceDir;
- } catch (PackageManager.NameNotFoundException e) {
- throw new VirtualMachineException("Package not found", e);
- }
- }
+ String apkPath = (mApkPath != null) ? mApkPath : findPayloadApk(packageManager);
try {
vsConfig.apk = ParcelFileDescriptor.open(new File(apkPath), MODE_READ_ONLY);
@@ -492,6 +487,45 @@
return vsConfig;
}
+ private String findPayloadApk(PackageManager packageManager) throws VirtualMachineException {
+ ApplicationInfo appInfo;
+ try {
+ appInfo =
+ packageManager.getApplicationInfo(
+ mPackageName, PackageManager.ApplicationInfoFlags.of(0));
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new VirtualMachineException("Package not found", e);
+ }
+
+ String[] splitApkPaths = appInfo.splitSourceDirs;
+ String[] abis = Build.SUPPORTED_64_BIT_ABIS;
+
+ // If there are split APKs, and we know the payload binary name, see if we can find a
+ // split APK containing the binary.
+ if (mPayloadBinaryName != null && splitApkPaths != null && abis.length != 0) {
+ String[] libraryNames = new String[abis.length];
+ for (int i = 0; i < abis.length; i++) {
+ libraryNames[i] = "lib/" + abis[i] + "/" + mPayloadBinaryName;
+ }
+
+ for (String path : splitApkPaths) {
+ try (ZipFile zip = new ZipFile(path)) {
+ for (String name : libraryNames) {
+ if (zip.getEntry(name) != null) {
+ Log.i(TAG, "Found payload in " + path);
+ return path;
+ }
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to scan split APK: " + path, e);
+ }
+ }
+ }
+
+ // This really is the path to the APK, not a directory.
+ return appInfo.sourceDir;
+ }
+
private int bytesToMebiBytes(long mMemoryBytes) {
long oneMebi = 1024 * 1024;
// We can't express requests for more than 2 exabytes, but then they're not going to succeed
@@ -593,7 +627,8 @@
/**
* Sets the absolute path of the APK containing the binary payload that will execute within
- * the VM. If not set explicitly, defaults to the primary APK of the context.
+ * the VM. If not set explicitly, defaults to the split APK containing the payload, if there
+ * is one, and otherwise the primary APK of the context.
*
* @hide
*/
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index dc59fff..bc4db6c 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -85,13 +85,6 @@
"microdroid_property_contexts",
"mke2fs.microdroid",
- // Adding more libs manually for unbundled build.
- // TODO(b/268557568) these should be added automatically.
- "libcrypto",
- "liblzma",
- "libc++",
- "libssl",
-
"libvm_payload", // used by payload to interact with microdroid manager
"prng_seeder_microdroid",
diff --git a/microdroid/init.rc b/microdroid/init.rc
index ce0cab4..70c22d4 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -98,7 +98,7 @@
mount rootfs rootfs / remount bind ro nodev
# TODO(b/185767624): change the hard-coded size?
- mount tmpfs tmpfs /data noatime nosuid nodev rw size=128M
+ mount tmpfs tmpfs /data noatime nosuid nodev noexec rw size=128M
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
diff --git a/microdroid/kdump/kernel/arm64/kernel-5.15 b/microdroid/kdump/kernel/arm64/kernel-5.15
index 0f2172b..28b0214 100644
--- a/microdroid/kdump/kernel/arm64/kernel-5.15
+++ b/microdroid/kdump/kernel/arm64/kernel-5.15
Binary files differ
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 18cf49d..495d3bb 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -19,9 +19,9 @@
"libbinder_rs",
"libbyteorder",
"libcap_rust",
+ "libciborium",
"libdiced_open_dice",
"libdiced_sample_inputs",
- "libdiced_utils",
"libglob",
"libhex",
"libitertools",
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index c3136e8..3a2a1e6 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -16,12 +16,14 @@
use anyhow::{anyhow, bail, Context, Error, Result};
use byteorder::{NativeEndian, ReadBytesExt};
+use ciborium::{cbor, ser};
use diced_open_dice::{
bcc_handover_parse, retry_bcc_main_flow, BccHandover, Config, DiceArtifacts, DiceMode, Hash,
Hidden, InputValues, OwnedDiceArtifacts,
};
use keystore2_crypto::ZVec;
use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
+use microdroid_metadata::PayloadMetadata;
use openssl::hkdf::hkdf;
use openssl::md::Md;
use std::fs;
@@ -157,3 +159,70 @@
}
}
}
+
+/// Returns a configuration descriptor of the given payload following the BCC's specification:
+/// https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
+/// {
+/// -70002: "Microdroid payload",
+/// ? -71000: tstr // payload_config_path
+/// ? -71001: PayloadConfig
+/// }
+/// PayloadConfig = {
+/// 1: tstr // payload_binary_name
+/// }
+pub fn format_payload_config_descriptor(payload_metadata: &PayloadMetadata) -> Result<Vec<u8>> {
+ const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
+
+ let config_descriptor_cbor_value = match payload_metadata {
+ PayloadMetadata::config_path(payload_config_path) => cbor!({
+ -70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
+ -71000 => payload_config_path
+ }),
+ PayloadMetadata::config(payload_config) => cbor!({
+ -70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
+ -71001 => {1 => payload_config.payload_binary_name}
+ }),
+ }
+ .context("Failed to build a CBOR Value from payload metadata")?;
+ let mut config_descriptor = Vec::new();
+ ser::into_writer(&config_descriptor_cbor_value, &mut config_descriptor)?;
+ Ok(config_descriptor)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use microdroid_metadata::PayloadConfig;
+
+ #[test]
+ fn payload_metadata_with_path_formats_correctly() -> Result<()> {
+ let payload_metadata = PayloadMetadata::config_path("/config_path".to_string());
+ let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
+ static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
+ 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
+ 0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
+ 0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
+ 0x68,
+ ];
+ assert_eq!(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+ Ok(())
+ }
+
+ #[test]
+ fn payload_metadata_with_config_formats_correctly() -> Result<()> {
+ let payload_config = PayloadConfig {
+ payload_binary_name: "payload_binary".to_string(),
+ ..Default::default()
+ };
+ let payload_metadata = PayloadMetadata::config(payload_config);
+ let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
+ static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
+ 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
+ 0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
+ 0x15, 0x58, 0xa1, 0x01, 0x6e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62,
+ 0x69, 0x6e, 0x61, 0x72, 0x79,
+ ];
+ assert_eq!(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+ Ok(())
+ }
+}
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 1148c31..f83753c 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -21,7 +21,7 @@
mod swap;
mod vm_payload_service;
-use crate::dice::{DiceDriver, derive_sealing_key};
+use crate::dice::{DiceDriver, derive_sealing_key, format_payload_config_descriptor};
use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
use crate::vm_payload_service::register_vm_payload_service;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
@@ -35,7 +35,6 @@
use apkverify::{get_public_key_der, verify, V4Signature};
use binder::Strong;
use diced_open_dice::OwnedDiceArtifacts;
-use diced_utils::cbor::{encode_header, encode_number};
use glob::glob;
use itertools::sorted;
use libc::VMADDR_CID_HOST;
@@ -287,54 +286,14 @@
let code_hash = code_hash_ctx.finish();
let authority_hash = authority_hash_ctx.finish();
- // {
- // -70002: "Microdroid payload",
- // ? -71000: tstr // payload_config_path
- // ? -71001: PayloadConfig
- // }
- // PayloadConfig = {
- // 1: tstr // payload_binary_name
- // }
-
- let mut config_desc = vec![
- 0xa2, // map(2)
- 0x3a, 0x00, 0x01, 0x11, 0x71, // -70002
- 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79,
- 0x6c, 0x6f, 0x61, 0x64, // "Microdroid payload"
- ];
-
- match payload_metadata {
- PayloadMetadata::config_path(payload_config_path) => {
- encode_negative_number(-71000, &mut config_desc)?;
- encode_tstr(payload_config_path, &mut config_desc)?;
- }
- PayloadMetadata::config(payload_config) => {
- encode_negative_number(-71001, &mut config_desc)?;
- encode_header(5, 1, &mut config_desc)?; // map(1)
- encode_number(1, &mut config_desc)?;
- encode_tstr(&payload_config.payload_binary_name, &mut config_desc)?;
- }
- }
+ let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
// Check debuggability, conservatively assuming it is debuggable
let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
// Send the details to diced
let hidden = verified_data.salt.clone().try_into().unwrap();
- dice.derive(code_hash, &config_desc, authority_hash, debuggable, hidden)
-}
-
-fn encode_tstr(tstr: &str, buffer: &mut Vec<u8>) -> Result<()> {
- let bytes = tstr.as_bytes();
- encode_header(3, bytes.len().try_into().unwrap(), buffer)?;
- buffer.extend_from_slice(bytes);
- Ok(())
-}
-
-fn encode_negative_number(n: i64, buffer: &mut dyn Write) -> Result<()> {
- ensure!(n < 0);
- let n = -1 - n;
- encode_header(1, n.try_into().unwrap(), buffer)
+ dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
}
fn is_strict_boot() -> bool {
@@ -721,6 +680,9 @@
// Use the salt from a verified instance, or generate a salt for a new instance.
let salt = if let Some(saved_data) = saved_data {
saved_data.salt.clone()
+ } else if is_strict_boot() {
+ // No need to add more entropy as a previous stage must have used a new, random salt.
+ vec![0u8; 64]
} else {
let mut salt = vec![0u8; 64];
salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index f209784..f62a580 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -216,12 +216,22 @@
}
/// Get slice containing the platform BCC.
- pub fn get_bcc_mut(&mut self) -> &mut [u8] {
- &mut self.body[self.bcc_range.clone()]
- }
+ pub fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
+ let bcc_start = self.bcc_range.start;
+ let bcc_end = self.bcc_range.len();
+ let (_, rest) = self.body.split_at_mut(bcc_start);
+ let (bcc, rest) = rest.split_at_mut(bcc_end);
- /// Get slice containing the platform debug policy.
- pub fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
- self.dp_range.as_ref().map(|r| &mut self.body[r.clone()])
+ let dp = if let Some(dp_range) = &self.dp_range {
+ let dp_start = dp_range.start.checked_sub(self.bcc_range.end).unwrap();
+ let dp_end = dp_range.len();
+ let (_, rest) = rest.split_at_mut(dp_start);
+ let (dp, _) = rest.split_at_mut(dp_end);
+ Some(dp)
+ } else {
+ None
+ };
+
+ (bcc, dp)
}
}
diff --git a/pvmfw/src/debug_policy.rs b/pvmfw/src/debug_policy.rs
index 4cb338d..23d3e1d 100644
--- a/pvmfw/src/debug_policy.rs
+++ b/pvmfw/src/debug_policy.rs
@@ -203,7 +203,7 @@
disable_ramdump(fdt)?;
}
- // Handles conseole output in the debug policy
+ // Handles console output in the debug policy
if is_console_output_enabled(fdt)? {
enable_console_output(fdt)?;
info!("console output is enabled by debug policy");
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index a1cd2a1..106a4ef 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -242,7 +242,7 @@
RebootReason::InvalidConfig
})?;
- let bcc_slice = appended.get_bcc_mut();
+ let (bcc_slice, debug_policy) = appended.get_entries();
debug!("Activating dynamic page table...");
// SAFETY - page_table duplicates the static mappings for everything that the Rust code is
@@ -266,7 +266,7 @@
// SAFETY - As we `?` the result, there is no risk of using a bad `slices.fdt`.
unsafe {
- handle_debug_policy(slices.fdt, appended.get_debug_policy()).map_err(|e| {
+ handle_debug_policy(slices.fdt, debug_policy).map_err(|e| {
error!("Unexpected error when handling debug policy: {e:?}");
RebootReason::from(e)
})?;
@@ -397,17 +397,10 @@
}
}
- fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
+ fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
match self {
- Self::Config(ref mut cfg) => cfg.get_debug_policy(),
- Self::LegacyBcc(_) => None,
- }
- }
-
- fn get_bcc_mut(&mut self) -> &mut [u8] {
- match self {
- Self::LegacyBcc(ref mut bcc) => bcc,
- Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
+ Self::Config(ref mut cfg) => cfg.get_entries(),
+ Self::LegacyBcc(ref mut bcc) => (bcc, None),
}
}
}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 48bab0c..d89e718 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -121,7 +121,7 @@
})?;
flush(next_bcc);
- let strict_boot = false; // TODO(b/268307476): Flip in its own commit to isolate testing.
+ let strict_boot = true;
modify_for_next_stage(fdt, next_bcc, new_instance, strict_boot).map_err(|e| {
error!("Failed to configure device tree: {e}");
RebootReason::InternalError
diff --git a/tests/aidl/Android.bp b/tests/aidl/Android.bp
index d59ca7e..ed4e8ff 100644
--- a/tests/aidl/Android.bp
+++ b/tests/aidl/Android.bp
@@ -6,6 +6,10 @@
name: "com.android.microdroid.testservice",
srcs: ["com/android/microdroid/testservice/**/*.aidl"],
unstable: true,
+ flags: [
+ "-Werror",
+ "-Wno-mixed-oneway",
+ ],
backend: {
java: {
gen_rpc: true,
diff --git a/tests/aidl/com/android/microdroid/testservice/IAppCallback.aidl b/tests/aidl/com/android/microdroid/testservice/IAppCallback.aidl
new file mode 100644
index 0000000..9859090
--- /dev/null
+++ b/tests/aidl/com/android/microdroid/testservice/IAppCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2023 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 com.android.microdroid.testservice;
+
+import com.android.microdroid.testservice.IVmCallback;
+
+/**
+ * An interface exposed by the app for callbacks from the VM.
+ *
+ * {@hide}
+ */
+interface IAppCallback {
+ /** Invites the app to call vmCallback#echoMessage() */
+ void setVmCallback(IVmCallback vmCallback);
+
+ /** Asynchronusly called by the VM in response to a call to echoMessage(). */
+ void onEchoRequestReceived(String message);
+}
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index 4611134..36c3aaf 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -15,7 +15,12 @@
*/
package com.android.microdroid.testservice;
-/** {@hide} */
+import com.android.microdroid.testservice.IAppCallback;
+
+/**
+ * This is the service exposed by the test payload, called by the test app.
+ * {@hide}
+ */
interface ITestService {
const long SERVICE_PORT = 5678;
@@ -56,6 +61,15 @@
/* get the content of the specified file. */
String readFromFile(String path);
+ /* get file permissions of the give file by stat'ing it */
+ int getFilePermissions(String path);
+
+ /** Returns flags for the given mountPoint. */
+ int getMountFlags(String mountPoint);
+
+ /** Requests the VM to asynchronously call appCallback.setVmCallback() */
+ void requestCallback(IAppCallback appCallback);
+
/**
* Request the service to exit, triggering the termination of the VM. This may cause any
* requests in flight to fail.
diff --git a/tests/aidl/com/android/microdroid/testservice/IVmCallback.aidl b/tests/aidl/com/android/microdroid/testservice/IVmCallback.aidl
new file mode 100644
index 0000000..617d184
--- /dev/null
+++ b/tests/aidl/com/android/microdroid/testservice/IVmCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2023 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 com.android.microdroid.testservice;
+
+/**
+ * An interface exposed by the VM for callbacks from the app.
+ *
+ * {@hide}
+ */
+interface IVmCallback {
+ /** Requests the VM to asynchronously call the app's onEchoRequestReceived() callback. */
+ void echoMessage(String message);
+}
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
index ba82c38..8a63578 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
@@ -20,12 +20,14 @@
/** This class can be used in both host tests and device tests to get the device properties. */
public final class DeviceProperties {
+
/** PropertyGetter is used to get the property associated to a given key. */
public interface PropertyGetter {
String getProperty(String key) throws Exception;
}
private static final String KEY_VENDOR_DEVICE = "ro.product.vendor.device";
+ private static final String KEY_BOARD_PLATFORM = "ro.board.platform";
private static final String KEY_BUILD_TYPE = "ro.build.type";
private static final String KEY_METRICS_TAG = "debug.hypervisor.metrics_tag";
@@ -51,6 +53,11 @@
return vendorDeviceName != null && vendorDeviceName.startsWith(CUTTLEFISH_DEVICE_PREFIX);
}
+ public boolean isGs101() {
+ String platform = getProperty(KEY_BOARD_PLATFORM);
+ return "gs101".equals(platform);
+ }
+
/**
* @return whether the device is user build.
*/
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index be3c1da..bff16a2 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -53,15 +53,27 @@
import java.util.concurrent.TimeUnit;
public abstract class MicrodroidDeviceTestBase {
+ private static final String TAG = "MicrodroidDeviceTestBase";
private final String MAX_PERFORMANCE_TASK_PROFILE = "CPUSET_SP_TOP_APP";
public static boolean isCuttlefish() {
- return DeviceProperties.create(SystemProperties::get).isCuttlefish();
+ return getDeviceProperties().isCuttlefish();
+ }
+
+ public static boolean isGs101() {
+ return getDeviceProperties().isGs101();
+ }
+
+ public static boolean isUserBuild() {
+ return getDeviceProperties().isUserBuild();
}
public static String getMetricPrefix() {
- return MetricsProcessor.getMetricPrefix(
- DeviceProperties.create(SystemProperties::get).getMetricsTag());
+ return MetricsProcessor.getMetricPrefix(getDeviceProperties().getMetricsTag());
+ }
+
+ private static DeviceProperties getDeviceProperties() {
+ return DeviceProperties.create(SystemProperties::get);
}
protected final void grantPermission(String permission) {
@@ -82,13 +94,22 @@
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
UiAutomation uiAutomation = instrumentation.getUiAutomation();
String cmd = "settaskprofile " + Os.gettid() + " " + MAX_PERFORMANCE_TASK_PROFILE;
- String out = runInShell("MicrodroidDeviceTestBase", uiAutomation, cmd).trim();
+ String out = runInShell(TAG, uiAutomation, cmd).trim();
String expect = "Profile " + MAX_PERFORMANCE_TASK_PROFILE + " is applied successfully!";
if (!expect.equals(out)) {
throw new IOException("Could not apply max performance task profile: " + out);
}
}
+ public final boolean getDebugPolicyBoolean(String debugPolicy) throws IOException {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ String debugPolicyFilePath = "/proc/device-tree" + debugPolicy;
+ String cmd = "su root xxd -p " + debugPolicyFilePath;
+ String dp = runInShell(TAG, uiAutomation, cmd).trim();
+ return "00000001".equals(dp);
+ }
+
private Context mCtx;
private boolean mProtectedVm;
@@ -443,6 +464,8 @@
public String mFileContent;
public byte[] mBcc;
public long[] mTimings;
+ public int mFileMode;
+ public int mMountFlags;
public void assertNoException() {
if (mException != null) {
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 0be6a62..1fa0afe 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -827,6 +827,7 @@
assumeTrue(
"Protected VMs are not supported",
getAndroidDevice().supportsMicrodroid(/*protectedVm=*/ true));
+ assumeTrue("Test requires adb unroot", getDevice().disableAdbRoot());
CommandRunner android = new CommandRunner(getDevice());
// Pull etc/microdroid.json
diff --git a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
index 1ada5a1..755613a 100644
--- a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
@@ -22,6 +22,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -56,7 +57,6 @@
@NonNull private static final String MICRODROID_DEBUG_FULL = "full";
@NonNull private static final String MICRODROID_CONFIG_PATH = "assets/vm_config_apex.json";
@NonNull private static final String MICRODROID_LOG_PATH = TEST_ROOT + "log.txt";
- @NonNull private static final String MICRODROID_CONSOLE_PATH = TEST_ROOT + "console.txt";
private static final int BOOT_COMPLETE_TIMEOUT_MS = 30000; // 30 seconds
private static final int CONSOLE_OUTPUT_WAIT_MS = 5000; // 5 seconds
@@ -92,6 +92,7 @@
assumeTrue(
"Skip if protected VMs are not supported",
mAndroidDevice.supportsMicrodroid(/* protectedVm= */ true));
+ assumeFalse("Test requires setprop for using custom pvmfw and adb root", isUserBuild());
mAndroidDevice.enableAdbRoot();
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index bafab53..fe8f5c9 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -31,6 +31,7 @@
"cbor-java",
"truth-prebuilt",
"compatibility-common-util-devicesidelib",
+ "measure_io_as_jar",
],
jni_libs: [
"MicrodroidTestNativeLib",
@@ -62,6 +63,7 @@
static_libs: [
"com.android.microdroid.testservice-ndk",
"libbase",
+ "libfstab",
"libfsverity_digests_proto_cc",
"liblog",
"libprotobuf-cpp-lite-ndk",
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 f3f1252..6e4694c 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -28,6 +28,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.TruthJUnit.assume;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assert.assertThrows;
import com.google.common.base.Strings;
@@ -45,17 +46,21 @@
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.SystemProperties;
+import android.system.OsConstants;
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineCallback;
import android.system.virtualmachine.VirtualMachineConfig;
import android.system.virtualmachine.VirtualMachineDescriptor;
import android.system.virtualmachine.VirtualMachineException;
import android.system.virtualmachine.VirtualMachineManager;
+import android.util.Log;
import com.android.compatibility.common.util.CddTest;
import com.android.microdroid.test.device.MicrodroidDeviceTestBase;
import com.android.microdroid.test.vmshare.IVmShareTestService;
+import com.android.microdroid.testservice.IAppCallback;
import com.android.microdroid.testservice.ITestService;
+import com.android.microdroid.testservice.IVmCallback;
import org.junit.After;
import org.junit.Before;
@@ -133,7 +138,7 @@
private static final String VM_SHARE_APP_PACKAGE_NAME = "com.android.microdroid.vmshare_app";
private void createAndConnectToVmHelper(int cpuTopology) throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -178,7 +183,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
public void createAndRunNoDebugVm() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
// For most of our tests we use a debug VM so failures can be diagnosed.
// But we do need non-debug VMs to work, so run one.
@@ -205,7 +210,7 @@
"9.17/C-1-4",
})
public void createVmRequiresPermission() {
- assumeSupportedKernel();
+ assumeSupportedDevice();
revokePermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION);
@@ -226,7 +231,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void autoCloseVm() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -311,7 +316,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void vmLifecycleChecks() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -360,7 +365,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void connectVsock() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -398,6 +403,59 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
+ public void binderCallbacksWork() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilder()
+ .setPayloadBinaryName("MicrodroidTestNativeLib.so")
+ .setMemoryBytes(minMemoryRequired())
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
+
+ String request = "Hello";
+ CompletableFuture<String> response = new CompletableFuture<>();
+
+ IAppCallback appCallback =
+ new IAppCallback.Stub() {
+ @Override
+ public void setVmCallback(IVmCallback vmCallback) {
+ // Do this on a separate thread to simulate an asynchronous trigger,
+ // and to make sure it doesn't happen in the context of an inbound binder
+ // call.
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ vmCallback.echoMessage(request);
+ } catch (Exception e) {
+ response.completeExceptionally(e);
+ }
+ }
+ }.start();
+ }
+
+ @Override
+ public void onEchoRequestReceived(String message) {
+ response.complete(message);
+ }
+ };
+
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (service, results) -> {
+ service.requestCallback(appCallback);
+ response.get(10, TimeUnit.SECONDS);
+ });
+ testResults.assertNoException();
+ assertThat(response.getNow("no response")).isEqualTo("Received: " + request);
+ }
+
+ @Test
+ @CddTest(requirements = {"9.17/C-1-1"})
public void vmConfigGetAndSetTests() {
// Minimal has as little as specified as possible; everything that can be is defaulted.
VirtualMachineConfig.Builder minimalBuilder = newVmConfigBuilder();
@@ -578,7 +636,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void vmmGetAndCreate() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -676,7 +734,7 @@
"9.17/C-1-4",
})
public void createVmWithConfigRequiresPermission() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -699,7 +757,7 @@
"9.17/C-1-1",
})
public void deleteVm() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -727,7 +785,7 @@
"9.17/C-1-1",
})
public void deleteVmFiles() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -760,7 +818,7 @@
"9.17/C-1-1",
})
public void validApkPathIsAccepted() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -797,7 +855,7 @@
"9.17/C-2-1"
})
public void extraApk() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
VirtualMachineConfig config =
@@ -865,7 +923,7 @@
}
private void changeDebugLevel(int fromLevel, int toLevel) throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig.Builder builder =
newVmConfigBuilder()
@@ -936,7 +994,7 @@
"9.17/C-2-7"
})
public void instancesOfSameVmHaveDifferentCdis() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
VirtualMachineConfig normalConfig =
@@ -962,7 +1020,7 @@
"9.17/C-2-7"
})
public void sameInstanceKeepsSameCdis() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
assume().withMessage("Skip on CF. Too Slow. b/257270529").that(isCuttlefish()).isFalse();
grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
@@ -987,7 +1045,7 @@
"9.17/C-2-7"
})
public void bccIsSuperficiallyWellFormed() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
VirtualMachineConfig normalConfig =
@@ -1026,7 +1084,7 @@
"9.17/C-1-2"
})
public void accessToCdisIsRestricted() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -1226,7 +1284,7 @@
@Test
public void importedVmAndOriginalVmHaveTheSameCdi() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
// Arrange
grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
VirtualMachineConfig config =
@@ -1325,7 +1383,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void encryptedStorageAvailable() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -1349,7 +1407,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void encryptedStorageIsInaccessibleToDifferentVm() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -1410,7 +1468,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
public void microdroidLauncherHasEmptyCapabilities() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
final VirtualMachineConfig vmConfig =
newVmConfigBuilder()
@@ -1435,7 +1493,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void encryptedStorageIsPersistent() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -1472,7 +1530,7 @@
@Test
@CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
public void canReadFileFromAssets_debugFull() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -1496,7 +1554,7 @@
@Test
public void outputShouldBeExplicitlyCaptured() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
final VirtualMachineConfig vmConfig =
new VirtualMachineConfig.Builder(getContext())
@@ -1517,6 +1575,22 @@
}
}
+ private boolean isConsoleOutputEnabledByDebugPolicy() {
+ if (isUserBuild()) {
+ Log.i(
+ TAG,
+ "Debug policy is inaccessible in user build. Assumes that console output is"
+ + " disabled");
+ return false;
+ }
+ try {
+ return getDebugPolicyBoolean("/avf/guest/common/log");
+ } catch (IOException e) {
+ Log.w(TAG, "Fail to read debug policy. Assumes false", e);
+ return false;
+ }
+ }
+
private boolean checkVmOutputIsRedirectedToLogcat(boolean debuggable) throws Exception {
String time =
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
@@ -1549,21 +1623,27 @@
@Test
public void outputIsRedirectedToLogcatIfNotCaptured() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
+ assumeFalse(
+ "Debug policy would turn on console output. Perhaps userdebug build?",
+ isConsoleOutputEnabledByDebugPolicy());
assertThat(checkVmOutputIsRedirectedToLogcat(true)).isTrue();
}
@Test
public void outputIsNotRedirectedToLogcatIfNotDebuggable() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
+ assumeFalse(
+ "Debug policy would turn on console output. Perhaps userdebug build?",
+ isConsoleOutputEnabledByDebugPolicy());
assertThat(checkVmOutputIsRedirectedToLogcat(false)).isFalse();
}
@Test
public void testStartVmWithPayloadOfAnotherApp() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
Context ctx = getContext();
Context otherAppCtx = ctx.createPackageContext(VM_SHARE_APP_PACKAGE_NAME, 0);
@@ -1591,7 +1671,7 @@
@Test
public void testVmDescriptorParcelUnparcel_noTrustedStorage() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -1625,7 +1705,7 @@
@Test
public void testVmDescriptorParcelUnparcel_withTrustedStorage() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
VirtualMachineConfig config =
newVmConfigBuilder()
@@ -1679,7 +1759,7 @@
@Test
public void testShareVmWithAnotherApp() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
Context ctx = getContext();
Context otherAppCtx = ctx.createPackageContext(VM_SHARE_APP_PACKAGE_NAME, 0);
@@ -1727,7 +1807,7 @@
@Test
public void testShareVmWithAnotherApp_encryptedStorage() throws Exception {
- assumeSupportedKernel();
+ assumeSupportedDevice();
Context ctx = getContext();
Context otherAppCtx = ctx.createPackageContext(VM_SHARE_APP_PACKAGE_NAME, 0);
@@ -1779,6 +1859,98 @@
}
}
+ @Test
+ @CddTest(requirements = {"9.17/C-1-5"})
+ public void testFileUnderBinHasExecutePermission() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig vmConfig =
+ newVmConfigBuilder()
+ .setPayloadBinaryName("MicrodroidTestNativeLib.so")
+ .setMemoryBytes(minMemoryRequired())
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_perms", vmConfig);
+
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mFileMode = ts.getFilePermissions("/mnt/apk/bin/measure_io");
+ });
+
+ testResults.assertNoException();
+ int allPermissionsMask =
+ OsConstants.S_IRUSR
+ | OsConstants.S_IWUSR
+ | OsConstants.S_IXUSR
+ | OsConstants.S_IRGRP
+ | OsConstants.S_IWGRP
+ | OsConstants.S_IXGRP
+ | OsConstants.S_IROTH
+ | OsConstants.S_IWOTH
+ | OsConstants.S_IXOTH;
+ assertThat(testResults.mFileMode & allPermissionsMask)
+ .isEqualTo(OsConstants.S_IRUSR | OsConstants.S_IXUSR);
+ }
+
+ // Taken from bionic/libs/kernel/uapi/linux/mounth.h.
+ private static final int MS_NOEXEC = 8;
+
+ @Test
+ @CddTest(requirements = {"9.17/C-1-5"})
+ public void dataIsMountedWithNoExec() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig vmConfig =
+ newVmConfigBuilder()
+ .setPayloadBinaryName("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_data_mount", vmConfig);
+
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mMountFlags = ts.getMountFlags("/data");
+ });
+
+ assertThat(testResults.mException).isNull();
+ assertWithMessage("/data should be mounted with MS_NOEXEC")
+ .that(testResults.mMountFlags & MS_NOEXEC)
+ .isEqualTo(MS_NOEXEC);
+ }
+
+ @Test
+ @CddTest(requirements = {"9.17/C-1-5"})
+ public void encryptedStoreIsMountedWithNoExec() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig vmConfig =
+ newVmConfigBuilder()
+ .setPayloadBinaryName("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .setEncryptedStorageBytes(4_000_000)
+ .build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_encstore_no_exec", vmConfig);
+
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mMountFlags = ts.getMountFlags("/mnt/encryptedstore");
+ });
+
+ assertThat(testResults.mException).isNull();
+ assertWithMessage("/mnt/encryptedstore should be mounted with MS_NOEXEC")
+ .that(testResults.mMountFlags & MS_NOEXEC)
+ .isEqualTo(MS_NOEXEC);
+ }
+
private static class VmShareServiceConnection implements ServiceConnection {
private final CountDownLatch mLatch = new CountDownLatch(1);
@@ -1848,10 +2020,16 @@
return 0;
}
- private void assumeSupportedKernel() {
+ private void assumeSupportedDevice() {
assume()
.withMessage("Skip on 5.4 kernel. b/218303240")
.that(KERNEL_VERSION)
.isNotEqualTo("5.4");
+
+ if (isProtectedVm()) {
+ assume().withMessage("Protected VMs not supported on gs101 devices. b/270841564")
+ .that(isGs101())
+ .isFalse();
+ }
}
}
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 19d6cd4..d24ddfd 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -15,12 +15,15 @@
*/
#include <aidl/com/android/microdroid/testservice/BnTestService.h>
+#include <aidl/com/android/microdroid/testservice/BnVmCallback.h>
+#include <aidl/com/android/microdroid/testservice/IAppCallback.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/result.h>
#include <android-base/scopeguard.h>
#include <android/log.h>
#include <fcntl.h>
+#include <fstab/fstab.h>
#include <fsverity_digests.pb.h>
#include <linux/vm_sockets.h>
#include <stdint.h>
@@ -40,8 +43,14 @@
using android::base::make_scope_guard;
using android::base::Result;
using android::base::unique_fd;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::GetEntryForMountPoint;
+using android::fs_mgr::ReadFstabFromFile;
using aidl::com::android::microdroid::testservice::BnTestService;
+using aidl::com::android::microdroid::testservice::BnVmCallback;
+using aidl::com::android::microdroid::testservice::IAppCallback;
using ndk::ScopedAStatus;
extern void testlib_sub();
@@ -139,7 +148,25 @@
}
Result<void> start_test_service() {
+ class VmCallbackImpl : public BnVmCallback {
+ private:
+ std::shared_ptr<IAppCallback> mAppCallback;
+
+ public:
+ explicit VmCallbackImpl(const std::shared_ptr<IAppCallback>& appCallback)
+ : mAppCallback(appCallback) {}
+
+ ScopedAStatus echoMessage(const std::string& message) override {
+ std::thread callback_thread{[=, appCallback = mAppCallback] {
+ appCallback->onEchoRequestReceived("Received: " + message);
+ }};
+ callback_thread.detach();
+ return ScopedAStatus::ok();
+ }
+ };
+
class TestService : public BnTestService {
+ public:
ScopedAStatus addInteger(int32_t a, int32_t b, int32_t* out) override {
*out = a + b;
return ScopedAStatus::ok();
@@ -221,7 +248,7 @@
return ScopedAStatus::ok();
}
- virtual ::ScopedAStatus runEchoReverseServer() override {
+ ScopedAStatus runEchoReverseServer() override {
auto result = start_echo_reverse_server();
if (result.ok()) {
return ScopedAStatus::ok();
@@ -251,6 +278,41 @@
return ScopedAStatus::ok();
}
+ ScopedAStatus getFilePermissions(const std::string& path, int32_t* out) override {
+ struct stat sb;
+ if (stat(path.c_str(), &sb) != -1) {
+ *out = sb.st_mode;
+ } else {
+ std::string msg = "stat " + path + " failed : " + std::strerror(errno);
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ msg.c_str());
+ }
+ return ScopedAStatus::ok();
+ }
+
+ ScopedAStatus getMountFlags(const std::string& mount_point, int32_t* out) override {
+ Fstab fstab;
+ if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "Failed to read /proc/mounts");
+ }
+ FstabEntry* entry = GetEntryForMountPoint(&fstab, mount_point);
+ if (entry == nullptr) {
+ std::string msg = mount_point + " not found in /proc/mounts";
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ msg.c_str());
+ }
+ *out = entry->flags;
+ return ScopedAStatus::ok();
+ }
+
+ ScopedAStatus requestCallback(const std::shared_ptr<IAppCallback>& appCallback) {
+ auto vmCallback = ndk::SharedRefBase::make<VmCallbackImpl>(appCallback);
+ std::thread callback_thread{[=] { appCallback->setVmCallback(vmCallback); }};
+ callback_thread.detach();
+ return ScopedAStatus::ok();
+ }
+
ScopedAStatus quit() override { exit(0); }
};
auto testService = ndk::SharedRefBase::make<TestService>();
diff --git a/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java b/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
index 278e1a2..edd6bf5 100644
--- a/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
+++ b/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
@@ -29,6 +29,7 @@
import com.android.microdroid.test.vmshare.IVmShareTestService;
import com.android.microdroid.testservice.ITestService;
+import com.android.microdroid.testservice.IAppCallback;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
@@ -230,6 +231,21 @@
}
@Override
+ public int getFilePermissions(String path) throws RemoteException {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public int getMountFlags(String path) throws RemoteException {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
+ public void requestCallback(IAppCallback appCallback) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
public void quit() throws RemoteException {
throw new UnsupportedOperationException("Not supported");
}
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 1781007..48e2431 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -20,6 +20,7 @@
use crate::composite::make_composite_image;
use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmContext, VmInstance, VmState};
use crate::debug_config::should_prepare_console_output;
+use crate::debug_config::is_ramdump_needed;
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
use crate::selinux::{getfilecon, SeContext};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
@@ -318,6 +319,17 @@
check_gdb_allowed(config)?;
}
+ let ramdump = if is_ramdump_needed(config) {
+ Some(prepare_ramdump_file(&temporary_directory)?)
+ } else {
+ None
+ };
+
+ let debug_level = match config {
+ VirtualMachineConfig::AppConfig(app_config) => app_config.debugLevel,
+ _ => DebugLevel::NONE,
+ };
+
let state = &mut *self.state.lock().unwrap();
let console_fd =
clone_or_prepare_logger_fd(config, console_fd, format!("Console({})", cid))?;
@@ -408,19 +420,6 @@
}
};
- // Creating this ramdump file unconditionally is not harmful as ramdump will be created
- // only when the VM is configured as such. `ramdump_write` is sent to crosvm and will
- // be the backing store for the /dev/hvc1 where VM will emit ramdump to. `ramdump_read`
- // will be sent back to the client (i.e. the VM owner) for readout.
- let ramdump_path = temporary_directory.join("ramdump");
- let ramdump = prepare_ramdump_file(&ramdump_path).map_err(|e| {
- error!("Failed to prepare ramdump file: {:?}", e);
- Status::new_service_specific_error_str(
- -1,
- Some(format!("Failed to prepare ramdump file: {:?}", e)),
- )
- })?;
-
// Actually start the VM.
let crosvm_config = CrosvmConfig {
cid,
@@ -431,13 +430,14 @@
disks,
params: config.params.to_owned(),
protected: *is_protected,
+ debug_level,
memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
cpus,
host_cpu_topology,
task_profiles: config.taskProfiles.clone(),
console_fd,
log_fd,
- ramdump: Some(ramdump),
+ ramdump,
indirect_files,
platform_version: parse_platform_version_req(&config.platformVersion)?,
detect_hangup: is_app_config,
@@ -486,10 +486,6 @@
part.flush()
}
-fn prepare_ramdump_file(ramdump_path: &Path) -> Result<File> {
- File::create(ramdump_path).context(format!("Failed to create ramdump file {:?}", &ramdump_path))
-}
-
fn round_up(input: u64, granularity: u64) -> u64 {
if granularity == 0 {
return input;
@@ -979,6 +975,22 @@
})
}
+/// Create the empty ramdump file
+fn prepare_ramdump_file(temporary_directory: &Path) -> binder::Result<File> {
+ // `ramdump_write` is sent to crosvm and will be the backing store for the /dev/hvc1 where
+ // VM will emit ramdump to. `ramdump_read` will be sent back to the client (i.e. the VM
+ // owner) for readout.
+ let ramdump_path = temporary_directory.join("ramdump");
+ let ramdump = File::create(ramdump_path).map_err(|e| {
+ error!("Failed to prepare ramdump file: {:?}", e);
+ Status::new_service_specific_error_str(
+ -1,
+ Some(format!("Failed to prepare ramdump file: {:?}", e)),
+ )
+ })?;
+ Ok(ramdump)
+}
+
fn is_protected(config: &VirtualMachineConfig) -> bool {
match config {
VirtualMachineConfig::RawConfig(config) => config.protectedVm,
@@ -1025,13 +1037,12 @@
return Ok(Some(clone_file(fd)?));
}
- if let VirtualMachineConfig::AppConfig(app_config) = config {
- if !should_prepare_console_output(app_config.debugLevel) {
- return Ok(None);
- }
- } else {
+ let VirtualMachineConfig::AppConfig(app_config) = config else {
return Ok(None);
- }
+ };
+ if !should_prepare_console_output(app_config.debugLevel) {
+ return Ok(None);
+ };
let (raw_read_fd, raw_write_fd) = pipe().map_err(|e| {
Status::new_service_specific_error_str(-1, Some(format!("Failed to create pipe: {:?}", e)))
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 1456d17..9db0971 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -16,6 +16,7 @@
use crate::aidl::{remove_temporary_files, Cid, VirtualMachineCallbacks};
use crate::atom::{get_num_cpus, write_vm_exited_stats};
+use crate::debug_config::should_prepare_console_output;
use anyhow::{anyhow, bail, Context, Error, Result};
use command_fds::CommandFdExt;
use lazy_static::lazy_static;
@@ -41,7 +42,10 @@
use std::time::{Duration, SystemTime};
use std::thread::{self, JoinHandle};
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
-use android_system_virtualizationservice::aidl::android::system::virtualizationservice::MemoryTrimLevel::MemoryTrimLevel;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ MemoryTrimLevel::MemoryTrimLevel,
+ VirtualMachineAppConfig::DebugLevel::DebugLevel
+};
use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IGlobalVmContext::IGlobalVmContext;
use binder::Strong;
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
@@ -68,6 +72,8 @@
const CROSVM_CRASH_STATUS: i32 = 33;
/// The exit status which crosvm returns when vcpu is stalled.
const CROSVM_WATCHDOG_REBOOT_STATUS: i32 = 36;
+/// The size of memory (in MiB) reserved for ramdump
+const RAMDUMP_RESERVED_MIB: u32 = 17;
const MILLIS_PER_SEC: i64 = 1000;
@@ -95,6 +101,7 @@
pub disks: Vec<DiskFile>,
pub params: Option<String>,
pub protected: bool,
+ pub debug_level: DebugLevel,
pub memory_mib: Option<NonZeroU32>,
pub cpus: Option<NonZeroU32>,
pub host_cpu_topology: bool,
@@ -530,6 +537,9 @@
/// Checks if ramdump has been created. If so, send it to tombstoned.
fn handle_ramdump(&self) -> Result<(), Error> {
let ramdump_path = self.temporary_directory.join("ramdump");
+ if !ramdump_path.as_path().try_exists()? {
+ return Ok(());
+ }
if std::fs::metadata(&ramdump_path)?.len() > 0 {
Self::send_ramdump_to_tombstoned(&ramdump_path)?;
}
@@ -665,24 +675,6 @@
}
}
-fn should_configure_ramdump(protected: bool) -> bool {
- if protected {
- // Protected VM needs ramdump configuration here.
- // pvmfw will disable ramdump if unnecessary.
- true
- } else {
- // For unprotected VM, ramdump should be handled here.
- // ramdump wouldn't be enabled if ramdump is explicitly set to <1>.
- if let Ok(mut file) = File::open("/proc/device-tree/avf/guest/common/ramdump") {
- let mut ramdump: [u8; 4] = Default::default();
- file.read_exact(&mut ramdump).map_err(|_| false).unwrap();
- // DT spec uses big endian although Android is always little endian.
- return u32::from_be_bytes(ramdump) == 1;
- }
- false
- }
-}
-
/// Starts an instance of `crosvm` to manage a new VM.
fn run_vm(
config: CrosvmConfig,
@@ -722,12 +714,32 @@
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;
+ let swiotlb_size_mib = 2 * virtio_pci_device_count as u32;
command.arg("--swiotlb").arg(swiotlb_size_mib.to_string());
// Workaround to keep crash_dump from trying to read protected guest memory.
// Context in b/238324526.
command.arg("--unmap-guest-memory-on-fork");
+
+ if config.ramdump.is_some() {
+ // Protected VM needs to reserve memory for ramdump here. pvmfw will drop This
+ // if ramdump should be disabled (via debug policy). Note that we reserve more
+ // memory for the restricted dma pool.
+ let ramdump_reserve = RAMDUMP_RESERVED_MIB + swiotlb_size_mib;
+ command.arg("--params").arg(format!("crashkernel={ramdump_reserve}M"));
+ }
+ } else {
+ if config.ramdump.is_some() {
+ command.arg("--params").arg(format!("crashkernel={RAMDUMP_RESERVED_MIB}M"));
+ }
+ if config.debug_level == DebugLevel::NONE
+ && should_prepare_console_output(config.debug_level)
+ {
+ // bootconfig.normal will be used, but we need log.
+ // pvmfw will add following commands by itself, but non-protected VM should do so here.
+ command.arg("--params").arg("printk.devkmsg=on");
+ command.arg("--params").arg("console=hvc0");
+ }
}
if let Some(memory_mib) = config.memory_mib {
@@ -816,10 +828,6 @@
debug!("Preserving FDs {:?}", preserved_fds);
command.preserved_fds(preserved_fds);
- if should_configure_ramdump(config.protected) {
- command.arg("--params").arg("crashkernel=17M");
- }
-
print_crosvm_args(&command);
let result = SharedChild::spawn(&mut command)?;
diff --git a/virtualizationmanager/src/debug_config.rs b/virtualizationmanager/src/debug_config.rs
index 332df08..a4ec419 100644
--- a/virtualizationmanager/src/debug_config.rs
+++ b/virtualizationmanager/src/debug_config.rs
@@ -15,20 +15,18 @@
//! Functions for AVF debug policy and debug level
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
- VirtualMachineAppConfig::DebugLevel::DebugLevel
+ VirtualMachineAppConfig::DebugLevel::DebugLevel, VirtualMachineConfig::VirtualMachineConfig,
};
use std::fs::File;
use std::io::Read;
/// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
fn get_debug_policy_bool(path: &'static str) -> Option<bool> {
- if let Ok(mut file) = File::open(path) {
- let mut log: [u8; 4] = Default::default();
- file.read_exact(&mut log).map_err(|_| false).unwrap();
- // DT spec uses big endian although Android is always little endian.
- return Some(u32::from_be_bytes(log) == 1);
- }
- None
+ let mut file = File::open(path).ok()?;
+ let mut log: [u8; 4] = Default::default();
+ file.read_exact(&mut log).ok()?;
+ // DT spec uses big endian although Android is always little endian.
+ Some(u32::from_be_bytes(log) == 1)
}
/// Get whether console output should be configred for VM to leave console and adb log.
@@ -37,3 +35,24 @@
debug_level != DebugLevel::NONE
|| get_debug_policy_bool("/proc/device-tree/avf/guest/common/log").unwrap_or_default()
}
+
+/// Decision to support ramdump
+pub fn is_ramdump_needed(config: &VirtualMachineConfig) -> bool {
+ let enabled_in_dp =
+ get_debug_policy_bool("/proc/device-tree/avf/guest/common/ramdump").unwrap_or_default();
+ let (protected, debuggable) = match config {
+ VirtualMachineConfig::RawConfig(config) => {
+ // custom VMs are considered debuggable for flexibility
+ (config.protectedVm, true)
+ }
+ VirtualMachineConfig::AppConfig(config) => {
+ (config.protectedVm, config.debugLevel == DebugLevel::FULL)
+ }
+ };
+
+ if protected {
+ enabled_in_dp
+ } else {
+ enabled_in_dp || debuggable
+ }
+}
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 5d785de..36edc64 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -152,7 +152,7 @@
}
fn find_empty_payload_apk_path() -> Result<PathBuf, Error> {
- const GLOB_PATTERN: &str = "/apex/com.android.virt/app/**/EmptyPayloadApp.apk";
+ const GLOB_PATTERN: &str = "/apex/com.android.virt/app/**/EmptyPayloadApp*.apk";
let mut entries: Vec<PathBuf> =
glob(GLOB_PATTERN).context("failed to glob")?.filter_map(|e| e.ok()).collect();
if entries.len() > 1 {
diff --git a/zipfuse/src/inode.rs b/zipfuse/src/inode.rs
index 3edbc49..ea63422 100644
--- a/zipfuse/src/inode.rs
+++ b/zipfuse/src/inode.rs
@@ -99,12 +99,8 @@
InodeData { mode, size: 0, data: InodeDataData::Directory(HashMap::new()) }
}
- fn new_file(zip_index: ZipIndex, zip_file: &zip::read::ZipFile) -> InodeData {
- InodeData {
- mode: zip_file.unix_mode().unwrap_or(DEFAULT_FILE_MODE),
- size: zip_file.size(),
- data: InodeDataData::File(zip_index),
- }
+ fn new_file(zip_index: ZipIndex, mode: u32, zip_file: &zip::read::ZipFile) -> InodeData {
+ InodeData { mode, size: zip_file.size(), data: InodeDataData::File(zip_index) }
}
fn add_to_directory(&mut self, name: CString, entry: DirectoryEntry) {
@@ -188,6 +184,16 @@
let mut parent = ROOT;
let mut iter = path.iter().peekable();
+
+ let mut file_mode = DEFAULT_FILE_MODE;
+ if path.starts_with("bin/") {
+ // Allow files under bin to have execute permission, this enables payloads to bundle
+ // additional binaries that they might want to execute.
+ // An example of such binary is measure_io one used in the authfs performance tests.
+ // More context available at b/265261525 and b/270955654.
+ file_mode |= libc::S_IXUSR;
+ }
+
while let Some(name) = iter.next() {
// TODO(jiyong): remove this check by canonicalizing `path`
if name == ".." {
@@ -211,8 +217,11 @@
}
// No inode found. Create a new inode and add it to the inode table.
+ // At the moment of writing this comment the apk file doesn't specify any
+ // permissions (apart from the ones on lib/), but it might change in the future.
+ // TODO(b/270955654): should we control the file permissions ourselves?
let inode = if is_file {
- InodeData::new_file(i, &file)
+ InodeData::new_file(i, file.unix_mode().unwrap_or(file_mode), &file)
} else if is_leaf {
InodeData::new_dir(file.unix_mode().unwrap_or(DEFAULT_DIR_MODE))
} else {