Merge "Re-enable PVM tests on Raviole"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 14452a3..d17b434 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -49,9 +49,6 @@
"path": "packages/modules/Virtualization/apkdmverity"
},
{
- "path": "packages/modules/Virtualization/avmd"
- },
- {
"path": "packages/modules/Virtualization/encryptedstore"
},
{
diff --git a/avmd/Android.bp b/avmd/Android.bp
deleted file mode 100644
index e5e0553..0000000
--- a/avmd/Android.bp
+++ /dev/null
@@ -1,61 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
- name: "libavmd_defaults",
- crate_name: "avmd",
- host_supported: true,
- srcs: ["src/lib.rs"],
- prefer_rlib: true,
- rustlibs: [
- "libhex",
- "libserde",
- "libapkverify",
- ],
-}
-
-rust_library {
- name: "libavmd",
- defaults: ["libavmd_defaults"],
-}
-
-rust_defaults {
- name: "avmdtool.defaults",
- srcs: ["src/main.rs"],
- host_supported: true,
- prefer_rlib: true,
- rustlibs: [
- "libanyhow",
- "libapexutil_rust",
- "libapkverify",
- "libavmd",
- "libclap",
- "libserde",
- "libserde_cbor",
- "libvbmeta_rust",
- ],
-}
-
-rust_binary {
- name: "avmdtool",
- defaults: ["avmdtool.defaults"],
-}
-
-rust_test {
- name: "avmdtool.test",
- defaults: ["avmdtool.defaults"],
- test_suites: ["general-tests"],
-}
-
-rust_test {
- name: "avmdtool_tests",
- srcs: ["tests/*_test.rs"],
- test_suites: ["general-tests"],
- rustlibs: [
- "libtempfile",
- ],
- compile_multilib: "first",
- data_bins: ["avmdtool"],
- data: ["tests/data/*"],
-}
diff --git a/avmd/README.md b/avmd/README.md
deleted file mode 100644
index ae813a0..0000000
--- a/avmd/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# The AVMD image format
----
-
-The AVMD image format is used to descibe the verified code that a VM will
-load. This repository contains tools and libraries for working with the AVMD
-image format.
-
-# What is it?
-
-When a VM boots, it loads and verifies a set of images that control execution
-within the VM. Therefore, describing what executes in a VM means describing
-what is loaded. The AVMD image format is designed, for this purpose, to
-describe the closure of images that can be loaded and how they should be
-verified.
-
-# Caveats
-
-The AVMD image format will only allow Android supported signing formats. The
-supported formats are currently limited to [AVB][] and [APK][].
-
-[AVB]: https://android.googlesource.com/platform/external/avb/+/master/README.md
-[APK]: https://source.android.com/security/apksigning#schemes
-
-Verification of the images as they are loaded is the responsibility of the VM.
-The VM is required to only load the images described and to verify them against
-the included parameters. If the VM does not follow this requirement, the
-description of the VM may not be accurate and must not be trusted. Validating
-that the VM behaves as expected requires audit of all boot stages of the VM.
-
-# Using avmdtool
-
-The `.avmd` file can be created as follows
-
-```bash
-avmdtool create /tmp/out.avmd \
- --vbmeta pvmfw preload u-boot.bin \
- --vbmeta uboot env_vbmeta disk1/vbmeta.imb \
- --vbmeta uboot vbmeta micordoid/vbmeta.img \
- --apk microdroid payload compos.apk \
- --apk microdroid extra_apk extra_apk.apk \
- --apex-payload microdroid art_apex art.apex
-```
-
-You can read the `.avmd` file with
-
-```bash
-avmdtool dump /tmp/out.avmd
-```
diff --git a/avmd/TEST_MAPPING b/avmd/TEST_MAPPING
deleted file mode 100644
index 892eb2c..0000000
--- a/avmd/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "avf-presubmit": [
- {
- "name": "avmdtool.test"
- },
- {
- "name": "avmdtool_tests"
- }
- ]
-}
diff --git a/avmd/src/avmd.rs b/avmd/src/avmd.rs
deleted file mode 100644
index cb02f39..0000000
--- a/avmd/src/avmd.rs
+++ /dev/null
@@ -1,154 +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.
-
-extern crate alloc;
-
-use alloc::{
- string::{String, ToString},
- vec::Vec,
-};
-use apkverify::SignatureAlgorithmID;
-use core::fmt;
-use serde::{Deserialize, Serialize};
-
-/// An Avmd struct contains
-/// - A header with version information that allows rollback when needed.
-/// - A list of descriptors that describe different images.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct Avmd {
- header: Header,
- descriptors: Vec<Descriptor>,
-}
-
-impl fmt::Display for Avmd {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, "Descriptors:")?;
- for descriptor in &self.descriptors {
- write!(f, "{}", descriptor)?;
- }
- Ok(())
- }
-}
-
-impl Avmd {
- /// Creates an instance of Avmd with a given list of descriptors.
- pub fn new(descriptors: Vec<Descriptor>) -> Avmd {
- Avmd { header: Header::default(), descriptors }
- }
-}
-
-static AVMD_MAGIC: u32 = 0x444d5641;
-static AVMD_VERSION_MAJOR: u16 = 1;
-static AVMD_VERSION_MINOR: u16 = 0;
-
-/// Header information for AVMD.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-struct Header {
- magic: u32,
- version_major: u16,
- version_minor: u16,
-}
-
-impl Default for Header {
- fn default() -> Self {
- Header {
- magic: AVMD_MAGIC,
- version_major: AVMD_VERSION_MAJOR,
- version_minor: AVMD_VERSION_MINOR,
- }
- }
-}
-
-/// AVMD descriptor.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub enum Descriptor {
- /// Descriptor type for the VBMeta images.
- VbMeta(VbMetaDescriptor),
- /// Descriptor type for APK.
- Apk(ApkDescriptor),
-}
-
-impl fmt::Display for Descriptor {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Descriptor::VbMeta(descriptor) => write!(f, "{}", descriptor),
- Descriptor::Apk(descriptor) => write!(f, "{}", descriptor),
- }
- }
-}
-
-/// VbMeta descriptor.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct VbMetaDescriptor {
- /// The identifier of this resource.
- #[serde(flatten)]
- pub resource: ResourceIdentifier,
- /// The SHA-512 [VBMeta digest][] calculated from the top-level VBMeta image.
- ///
- /// [VBMeta digest]: https://android.googlesource.com/platform/external/avb/+/master/README.md#the-vbmeta-digest
- pub vbmeta_digest: Vec<u8>,
-}
-
-impl fmt::Display for VbMetaDescriptor {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, " VBMeta descriptor:")?;
- writeln!(f, " namespace: {}", self.resource.namespace)?;
- writeln!(f, " name: {}", self.resource.name)?;
- writeln!(f, " vbmeta digest: {}", hex::encode(&self.vbmeta_digest))?;
- Ok(())
- }
-}
-
-/// APK descriptor.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct ApkDescriptor {
- /// The identifier of this resource.
- #[serde(flatten)]
- pub resource: ResourceIdentifier,
- /// The ID of the algoithm used to sign the APK.
- /// It should be one of the algorithms in the [list][].
- ///
- /// [list]: https://source.android.com/security/apksigning/v2#signature-algorithm-ids
- pub signature_algorithm_id: SignatureAlgorithmID,
- /// Digest of the APK's v3 signing block. TODO: fix
- pub apk_digest: Vec<u8>,
-}
-
-impl fmt::Display for ApkDescriptor {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- writeln!(f, " APK descriptor:")?;
- writeln!(f, " namespace: {}", self.resource.namespace)?;
- writeln!(f, " name: {}", self.resource.name)?;
- writeln!(f, " Signing algorithm ID: {:#04x}", self.signature_algorithm_id.to_u32())?;
- writeln!(f, " APK digest: {}", hex::encode(&self.apk_digest))?;
- Ok(())
- }
-}
-
-/// Resource identifier regroups information to identify resources.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct ResourceIdentifier {
- /// Namespace of the resource.
- namespace: String,
- /// Name of the resource.
- name: String,
-}
-
-impl ResourceIdentifier {
- /// Creates an instance of ResourceIdentifier with the given
- /// namespace and name.
- pub fn new(namespace: &str, name: &str) -> ResourceIdentifier {
- ResourceIdentifier { namespace: namespace.to_string(), name: name.to_string() }
- }
-}
diff --git a/avmd/src/lib.rs b/avmd/src/lib.rs
deleted file mode 100644
index 7a06e6a..0000000
--- a/avmd/src/lib.rs
+++ /dev/null
@@ -1,21 +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.
-
-//! Library for handling AVMD blobs.
-
-#![no_std]
-
-mod avmd;
-
-pub use avmd::{ApkDescriptor, Avmd, Descriptor, ResourceIdentifier, VbMetaDescriptor};
diff --git a/avmd/src/main.rs b/avmd/src/main.rs
deleted file mode 100644
index 8d7cb57..0000000
--- a/avmd/src/main.rs
+++ /dev/null
@@ -1,176 +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.
-
-//! Tool for handling AVMD blobs.
-
-use anyhow::{anyhow, bail, Result};
-use apexutil::get_payload_vbmeta_image_hash;
-use apkverify::get_apk_digest;
-use avmd::{ApkDescriptor, Avmd, Descriptor, ResourceIdentifier, VbMetaDescriptor};
-use clap::{
- builder::ValueParser,
- parser::{Indices, ValuesRef},
- Arg, ArgAction, ArgMatches, Command,
-};
-use serde::ser::Serialize;
-use std::{fs::File, path::PathBuf};
-use vbmeta::VbMetaImage;
-
-fn get_vbmeta_image_hash(file: &str) -> Result<Vec<u8>> {
- let img = VbMetaImage::verify_path(file)?;
- Ok(img.hash().ok_or_else(|| anyhow!("No hash as VBMeta image isn't signed"))?.to_vec())
-}
-
-/// Iterate over a set of argument values, that could be empty or come in
-/// (<index>, <namespace>, <name>, <file>) tuple.
-struct NamespaceNameFileIterator<'a> {
- indices: Option<Indices<'a>>,
- values: Option<ValuesRef<'a, String>>,
-}
-
-impl<'a> NamespaceNameFileIterator<'a> {
- fn new(args: &'a ArgMatches, name: &'a str) -> Self {
- NamespaceNameFileIterator { indices: args.indices_of(name), values: args.get_many(name) }
- }
-}
-
-impl<'a> Iterator for NamespaceNameFileIterator<'a> {
- type Item = (usize, &'a str, &'a str, &'a str);
-
- fn next(&mut self) -> Option<Self::Item> {
- match (self.indices.as_mut(), self.values.as_mut()) {
- (Some(indices), Some(values)) => {
- match (indices.nth(2), values.next(), values.next(), values.next()) {
- (Some(index), Some(namespace), Some(name), Some(file)) => {
- Some((index, namespace, name, file))
- }
- _ => None,
- }
- }
- _ => None,
- }
- }
-}
-
-fn create(args: &ArgMatches) -> Result<()> {
- // Store descriptors in the order they were given in the arguments
- // TODO: instead, group them by namespace?
- let mut descriptors = std::collections::BTreeMap::new();
- for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "vbmeta") {
- descriptors.insert(
- i,
- Descriptor::VbMeta(VbMetaDescriptor {
- resource: ResourceIdentifier::new(namespace, name),
- vbmeta_digest: get_vbmeta_image_hash(file)?,
- }),
- );
- }
- for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "apk") {
- let file = File::open(file)?;
- let (signature_algorithm_id, apk_digest) = get_apk_digest(file, /*verify=*/ true)?;
- descriptors.insert(
- i,
- Descriptor::Apk(ApkDescriptor {
- resource: ResourceIdentifier::new(namespace, name),
- signature_algorithm_id,
- apk_digest: apk_digest.to_vec(),
- }),
- );
- }
- for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "apex-payload") {
- descriptors.insert(
- i,
- Descriptor::VbMeta(VbMetaDescriptor {
- resource: ResourceIdentifier::new(namespace, name),
- vbmeta_digest: get_payload_vbmeta_image_hash(file)?,
- }),
- );
- }
- let avmd = Avmd::new(descriptors.into_values().collect());
- let mut bytes = Vec::new();
- avmd.serialize(
- &mut serde_cbor::Serializer::new(&mut serde_cbor::ser::IoWrite::new(&mut bytes))
- .packed_format()
- .legacy_enums(),
- )?;
- std::fs::write(args.get_one::<PathBuf>("file").unwrap(), &bytes)?;
- Ok(())
-}
-
-fn dump(args: &ArgMatches) -> Result<()> {
- let file = std::fs::read(args.get_one::<PathBuf>("file").unwrap())?;
- let avmd: Avmd = serde_cbor::from_slice(&file)?;
- println!("{}", avmd);
- Ok(())
-}
-
-fn clap_command() -> Command {
- let namespace_name_file = ["namespace", "name", "file"];
-
- Command::new("avmdtool")
- .subcommand_required(true)
- .arg_required_else_help(true)
- .subcommand(
- Command::new("create")
- .arg_required_else_help(true)
- .arg(Arg::new("file").value_parser(ValueParser::path_buf()).required(true))
- .arg(
- Arg::new("vbmeta")
- .long("vbmeta")
- .value_names(namespace_name_file)
- .num_args(3)
- .action(ArgAction::Append),
- )
- .arg(
- Arg::new("apk")
- .long("apk")
- .value_names(namespace_name_file)
- .num_args(3)
- .action(ArgAction::Append),
- )
- .arg(
- Arg::new("apex-payload")
- .long("apex-payload")
- .value_names(namespace_name_file)
- .num_args(3)
- .action(ArgAction::Append),
- ),
- )
- .subcommand(
- Command::new("dump")
- .arg_required_else_help(true)
- .arg(Arg::new("file").value_parser(ValueParser::path_buf()).required(true)),
- )
-}
-
-fn main() -> Result<()> {
- let args = clap_command().get_matches();
- match args.subcommand() {
- Some(("create", sub_args)) => create(sub_args)?,
- Some(("dump", sub_args)) => dump(sub_args)?,
- _ => bail!("Invalid arguments"),
- }
- Ok(())
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn verify_command() {
- // Check that the command parsing has been configured in a valid way.
- clap_command().debug_assert();
- }
-}
diff --git a/avmd/tests/avmdtool_test.rs b/avmd/tests/avmdtool_test.rs
deleted file mode 100644
index 4647f06..0000000
--- a/avmd/tests/avmdtool_test.rs
+++ /dev/null
@@ -1,65 +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.
-
-//! Tests for avmdtool.
-
-use std::fs;
-use std::process::Command;
-use tempfile::TempDir;
-
-#[test]
-fn test_dump() {
- let filename = "tests/data/test.avmd";
- assert!(
- fs::metadata(filename).is_ok(),
- "File '{}' does not exist. You can re-create it with:
- avmdtool create {} \\
- --apex-payload microdroid vbmeta tests/data/test.apex \\
- --apk microdroid_manager apk \\
- tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk \\
- --apk microdroid_manager extra-apk tests/data/v3-only-with-stamp.apk",
- filename,
- filename
- );
- let output = Command::new("./avmdtool").args(["dump", filename]).output().unwrap();
- assert!(output.status.success());
- assert_eq!(output.stdout, fs::read("tests/data/test.avmd.dump").unwrap());
-}
-
-#[test]
-fn test_create() {
- let test_dir = TempDir::new().unwrap();
- let test_file_path = test_dir.path().join("tmp_test.amvd");
- let output = Command::new("./avmdtool")
- .args([
- "create",
- test_file_path.to_str().unwrap(),
- "--apex-payload",
- "microdroid",
- "vbmeta",
- "tests/data/test.apex",
- "--apk",
- "microdroid_manager",
- "apk",
- "tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk",
- "--apk",
- "microdroid_manager",
- "extra-apk",
- "tests/data/v3-only-with-stamp.apk",
- ])
- .output()
- .unwrap();
- assert!(output.status.success());
- assert_eq!(fs::read(test_file_path).unwrap(), fs::read("tests/data/test.avmd").unwrap());
-}
diff --git a/avmd/tests/data/test.apex b/avmd/tests/data/test.apex
deleted file mode 100644
index fd79365..0000000
--- a/avmd/tests/data/test.apex
+++ /dev/null
Binary files differ
diff --git a/avmd/tests/data/test.avmd b/avmd/tests/data/test.avmd
deleted file mode 100644
index e567125..0000000
--- a/avmd/tests/data/test.avmd
+++ /dev/null
Binary files differ
diff --git a/avmd/tests/data/test.avmd.dump b/avmd/tests/data/test.avmd.dump
deleted file mode 100644
index a63a151..0000000
--- a/avmd/tests/data/test.avmd.dump
+++ /dev/null
@@ -1,16 +0,0 @@
-Descriptors:
- VBMeta descriptor:
- namespace: microdroid
- name: vbmeta
- vbmeta digest: 296e32a76544de9da01713e471403ab4667705ad527bb4f1fac0cf61e7ce122d
- APK descriptor:
- namespace: microdroid_manager
- name: apk
- Signing algorithm ID: 0x103
- APK digest: 0df2426ea33aedaf495d88e5be0c6a1663ff0a81c5ed12d5b2929ae4b4300f2f
- APK descriptor:
- namespace: microdroid_manager
- name: extra-apk
- Signing algorithm ID: 0x201
- APK digest: 626bb647c0089717a7ffa52fd8e845f9403d5e27f7a5a8752e47b3345fb82f5c
-
diff --git a/avmd/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk b/avmd/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk
deleted file mode 100644
index 0c9391c..0000000
--- a/avmd/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk
+++ /dev/null
Binary files differ
diff --git a/avmd/tests/data/v3-only-with-stamp.apk b/avmd/tests/data/v3-only-with-stamp.apk
deleted file mode 100644
index 5f65214..0000000
--- a/avmd/tests/data/v3-only-with-stamp.apk
+++ /dev/null
Binary files differ
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/tracing.md b/docs/debug/tracing.md
new file mode 100644
index 0000000..7d7ea0c
--- /dev/null
+++ b/docs/debug/tracing.md
@@ -0,0 +1,75 @@
+# Hypervisor & guest tracing
+
+## Hypervisor tracing
+
+Starting with android14-5.15 kernel it is possible to get traces from the hypervisor.
+
+### User space interface
+
+The user space hypervisor tracing interface is located either at /sys/kernel/tracing/hyp or at
+/sys/kernel/debug/tracing/hyp. On the Android phones it will usually be /sys/kernel/tracing/hyp,
+while on QEMU it will be /sys/kernel/debug/tracing/hyp.
+
+The user space interface is very similar to the ftrace user space interface, however there are some
+differences, e.g.:
+
+* Only boot clock is supported, and there is no way for user space to change the tracing_clock.
+* Hypervisor tracing periodically polls the data from the hypervisor, this is different from the
+ regular ftrace instance which pushes the events into the ring buffer.
+
+Note: the list above is not exhaustive.
+
+TODO(b/271412868): add more documentation on the user space interface.
+
+### Perfetto integration
+
+[Perfetto](https://perfetto.dev/docs/) is an open-source stack for performance instrumentation and
+trace analysis widely used in Android. Perfetto supports capturing and visualizing hypervisor
+traces.
+
+#### Capturing hypervisor traces on Android
+
+Consider first familiarizing yourself with Perfetto documentation for recording traces on Android:
+https://perfetto.dev/docs/quickstart/android-tracing.
+
+So far it is only possible to capture hypervisor trace events by providing the full trace config
+file to Perfetto. Here is the minimal
+
+```shell
+cat<<EOF>config.pbtx
+duration_ms: 10000
+
+buffers: {
+ size_kb: 8960
+ fill_policy: DISCARD
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ instance_name: "hyp"
+ ftrace_events: "hyp/hyp_enter"
+ ftrace_events: "hyp/hyp_exit"
+ }
+ }
+}
+EOF
+
+./record_android_trace -c config.pbtx -o trace_file.perfetto-trace
+```
+
+If you have an Android tree checked out, then record_android_trace helper script can be located at
+${REPO_ROOT}/external/perfetto/tools/record_android_traces. Otherwise, you can download the script
+by following steps outlined in the [Perfetto docs](
+https://perfetto.dev/docs/quickstart/android-tracing#recording-a-trace-through-the-cmdline)
+
+#### Capturing hypervisor traces on QEMU
+
+TODO(b/271412868): fill in this section
+
+TODO(b/271412868): Stay tuned, more docs coming soon!
+
+## Microdroid VM tracing
+
+TODO(b/271412868): Stay tuned, more docs are coming soon!
diff --git a/javalib/jni/android_system_virtualmachine_VirtualizationService.cpp b/javalib/jni/android_system_virtualmachine_VirtualizationService.cpp
index bd80880..fbd1fd5 100644
--- a/javalib/jni/android_system_virtualmachine_VirtualizationService.cpp
+++ b/javalib/jni/android_system_virtualmachine_VirtualizationService.cpp
@@ -29,7 +29,7 @@
using namespace android::base;
static constexpr const char VIRTMGR_PATH[] = "/apex/com.android.virt/bin/virtmgr";
-static constexpr size_t VIRTMGR_THREADS = 16;
+static constexpr size_t VIRTMGR_THREADS = 2;
extern "C" JNIEXPORT jint JNICALL
Java_android_system_virtualmachine_VirtualizationService_nativeSpawn(
@@ -83,7 +83,6 @@
ARpcSession_setFileDescriptorTransportMode(session.get(),
ARpcSession_FileDescriptorTransportMode::Unix);
ARpcSession_setMaxIncomingThreads(session.get(), VIRTMGR_THREADS);
- ARpcSession_setMaxOutgoingThreads(session.get(), VIRTMGR_THREADS);
// SAFETY - ARpcSession_setupUnixDomainBootstrapClient does not take ownership of clientFd.
auto client = ARpcSession_setupUnixDomainBootstrapClient(session.get(), clientFd);
return AIBinder_toJavaBinder(env, client);
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 668d7dc..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
*/
@@ -445,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);
@@ -495,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
@@ -596,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/libs/apkverify/src/algorithms.rs b/libs/apkverify/src/algorithms.rs
index 442b47c..c05ab38 100644
--- a/libs/apkverify/src/algorithms.rs
+++ b/libs/apkverify/src/algorithms.rs
@@ -204,9 +204,10 @@
}
/// Hash algorithms.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive, Default)]
#[repr(u32)]
pub enum HashAlgorithm {
+ #[default]
/// SHA-256
SHA256 = 1,
}
@@ -217,9 +218,3 @@
Self::from_u32(val).context(format!("Unsupported hash algorithm: {}", val))
}
}
-
-impl Default for HashAlgorithm {
- fn default() -> Self {
- HashAlgorithm::SHA256
- }
-}
diff --git a/libs/apkverify/src/v4.rs b/libs/apkverify/src/v4.rs
index 94abf99..e77ad77 100644
--- a/libs/apkverify/src/v4.rs
+++ b/libs/apkverify/src/v4.rs
@@ -104,9 +104,10 @@
}
/// Version of the idsig file format
-#[derive(Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
+#[derive(Debug, PartialEq, Eq, FromPrimitive, ToPrimitive, Default)]
#[repr(u32)]
pub enum Version {
+ #[default]
/// Version 2, the only supported version.
V2 = 2,
}
@@ -117,12 +118,6 @@
}
}
-impl Default for Version {
- fn default() -> Self {
- Version::V2
- }
-}
-
impl V4Signature<fs::File> {
/// Creates a `V4Signature` struct from the given idsig path.
pub fn from_idsig_path<P: AsRef<Path>>(idsig_path: P) -> Result<Self> {
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index bc4db6c..a2a4138 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -51,6 +51,7 @@
deps: [
"init_second_stage",
"microdroid_build_prop",
+ "microdroid_init_debug_policy",
"microdroid_init_rc",
"microdroid_ueventd_rc",
"microdroid_launcher",
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 70c22d4..5187a12 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -21,13 +21,9 @@
write /linkerconfig/ld.config.txt \#
chmod 644 /linkerconfig/ld.config.txt
-# If VM is debuggable, send logs to outside ot the VM via the serial console.
-# If non-debuggable, logs are internally consumed at /dev/null
-on early-init && property:ro.boot.microdroid.debuggable=1
- setprop ro.log.file_logger.path /dev/hvc2
-
-on early-init && property:ro.boot.microdroid.debuggable=0
- setprop ro.log.file_logger.path /dev/null
+ # Applies debug policy to decide whether to enable adb, adb root, and logcat.
+ # We don't directly exec the binary to specify stdio_to_kmsg.
+ exec_start init_debug_policy
on init
mkdir /mnt/apk 0755 system system
@@ -47,8 +43,6 @@
# payloads are not designed to run with bootstrap bionic
setprop apex_config.done true
- setprop ro.debuggable ${ro.boot.microdroid.debuggable:-0}
-
on property:microdroid_manager.init_done=1
# Stop ueventd to save memory
stop ueventd
@@ -57,7 +51,7 @@
# Mount tracefs (with GID=AID_READTRACEFS)
mount tracefs tracefs /sys/kernel/tracing gid=3012
-on init && property:ro.boot.adb.enabled=1
+on property:init_debug_policy.adbd.enabled=1
start adbd
# Mount filesystems and start core system services.
@@ -179,3 +173,8 @@
group shell log readproc
seclabel u:r:shell:s0
setenv HOSTNAME console
+
+service init_debug_policy /system/bin/init_debug_policy
+ oneshot
+ disabled
+ stdio_to_kmsg
diff --git a/microdroid/init_debug_policy/Android.bp b/microdroid/init_debug_policy/Android.bp
new file mode 100644
index 0000000..b56ef79
--- /dev/null
+++ b/microdroid/init_debug_policy/Android.bp
@@ -0,0 +1,11 @@
+rust_binary {
+ name: "microdroid_init_debug_policy",
+ srcs: ["src/init_debug_policy.rs"],
+ stem: "init_debug_policy",
+ rustlibs: [
+ "librustutils",
+ ],
+ installable: false, // match with microdroid_init_rc.
+ bootstrap: true,
+ prefer_rlib: true,
+}
diff --git a/microdroid/init_debug_policy/src/init_debug_policy.rs b/microdroid/init_debug_policy/src/init_debug_policy.rs
new file mode 100644
index 0000000..6c80926
--- /dev/null
+++ b/microdroid/init_debug_policy/src/init_debug_policy.rs
@@ -0,0 +1,57 @@
+// 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.
+
+//! Applies debug policies when booting microdroid
+
+use rustutils::system_properties;
+use rustutils::system_properties::PropertyWatcherError;
+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> {
+ 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)
+}
+
+fn main() -> Result<(), PropertyWatcherError> {
+ // If VM is debuggable or debug policy says so, send logs to outside ot the VM via the serial console.
+ // Otherwise logs are internally consumed at /dev/null
+ let log_path = if system_properties::read_bool("ro.boot.microdroid.debuggable", false)?
+ || get_debug_policy_bool("/sys/firmware/devicetree/base/avf/guest/common/log")
+ .unwrap_or_default()
+ {
+ "/dev/hvc2"
+ } else {
+ "/dev/null"
+ };
+ system_properties::write("ro.log.file_logger.path", log_path)?;
+
+ let (adbd_enabled, debuggable) = if system_properties::read_bool("ro.boot.adb.enabled", false)?
+ || get_debug_policy_bool("/sys/firmware/devicetree/base/avf/guest/microdroid/adb")
+ .unwrap_or_default()
+ {
+ // debuggable is required for adb root and bypassing adb authorization.
+ ("1", "1")
+ } else {
+ ("0", "0")
+ };
+ system_properties::write("init_debug_policy.adbd.enabled", adbd_enabled)?;
+ system_properties::write("ro.debuggable", debuggable)?;
+
+ Ok(())
+}
diff --git a/microdroid/payload/config/src/lib.rs b/microdroid/payload/config/src/lib.rs
index 925a543..cdef3e4 100644
--- a/microdroid/payload/config/src/lib.rs
+++ b/microdroid/payload/config/src/lib.rs
@@ -64,10 +64,11 @@
/// Payload's task can be one of plain executable
/// or an .so library which can be started via /system/bin/microdroid_launcher
-#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, Default)]
pub enum TaskType {
/// Task's command indicates the path to the executable binary.
#[serde(rename = "executable")]
+ #[default]
Executable,
/// Task's command indicates the .so library in /mnt/apk/lib/{arch}
#[serde(rename = "microdroid_launcher")]
@@ -87,12 +88,6 @@
pub command: String,
}
-impl Default for TaskType {
- fn default() -> TaskType {
- TaskType::Executable
- }
-}
-
/// APEX config
/// For now, we only pass the name of APEX.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
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 a464163..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 {
diff --git a/pvmfw/README.md b/pvmfw/README.md
index 1e4b605..c652f01 100644
--- a/pvmfw/README.md
+++ b/pvmfw/README.md
@@ -61,13 +61,27 @@
Starting in Android T, the `PRODUCT_BUILD_PVMFW_IMAGE` build variable controls
the generation of `pvmfw.img`, a new [ABL partition][ABL-part] containing the
-pvmfw binary and following the internal format of the [`boot`][boot-img]
-partition, intended to be verified and loaded by ABL on AVF-compatible devices.
+pvmfw binary (sometimes called "`pvmfw.bin`") and following the internal format
+of the [`boot`][boot-img] partition, intended to be verified and loaded by ABL
+on AVF-compatible devices.
+
+Once ABL has verified the `pvmfw.img` chained static partition, the contained
+[`boot.img` header][boot-img] may be used to obtain the size of the `pvmfw.bin`
+image (recorded in the `kernel_size` field), as it already does for the kernel
+itself. In accordance with the header format, the `kernel_size` bytes of the
+partition following the header will be the `pvmfw.bin` image.
+
+Note that when it gets executed in the context of a pVM, `pvmfw` expects to have
+been loaded at 4KiB-aligned intermediate physical address (IPA) so if ABL loads
+the `pvmfw.bin` image without respecting this alignment, it is the
+responsibility of the hypervisor to either reject the image or copy it into
+guest address space with the right alignment.
To support pKVM, ABL is expected to describe the region using a reserved memory
device tree node where both address and size have been properly aligned to the
-page size used by the hypervisor. For example, the following node describes a
-region of size `0x40000` at address `0x80000000`:
+page size used by the hypervisor. This single region must include both the pvmfw
+binary image and its configuration data (see below). For example, the following
+node describes a region of size `0x40000` at address `0x80000000`:
```
reserved-memory {
...
diff --git a/pvmfw/src/rand.rs b/pvmfw/src/rand.rs
index a53cac6..bf0edd5 100644
--- a/pvmfw/src/rand.rs
+++ b/pvmfw/src/rand.rs
@@ -52,12 +52,11 @@
fn fill_with_entropy(s: &mut [u8]) -> Result<()> {
const MAX_BYTES_PER_CALL: usize = size_of::<hvc::TrngRng64Entropy>();
- let bits = usize::try_from(u8::BITS).unwrap();
let (aligned, remainder) = s.split_at_mut(s.len() - s.len() % MAX_BYTES_PER_CALL);
for chunk in aligned.chunks_exact_mut(MAX_BYTES_PER_CALL) {
- let (r, s, t) = hvc::trng_rnd64((chunk.len() * bits).try_into().unwrap())?;
+ let (r, s, t) = repeat_trng_rnd(chunk.len())?;
let mut words = chunk.chunks_exact_mut(size_of::<u64>());
words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
@@ -67,7 +66,7 @@
if !remainder.is_empty() {
let mut entropy = [0; MAX_BYTES_PER_CALL];
- let (r, s, t) = hvc::trng_rnd64((remainder.len() * bits).try_into().unwrap())?;
+ let (r, s, t) = repeat_trng_rnd(remainder.len())?;
let mut words = entropy.chunks_exact_mut(size_of::<u64>());
words.next().unwrap().clone_from_slice(&t.to_ne_bytes());
@@ -80,6 +79,17 @@
Ok(())
}
+fn repeat_trng_rnd(n_bytes: usize) -> hvc::trng::Result<hvc::TrngRng64Entropy> {
+ let bits = usize::try_from(u8::BITS).unwrap();
+ let n_bits = (n_bytes * bits).try_into().unwrap();
+ loop {
+ match hvc::trng_rnd64(n_bits) {
+ Err(hvc::trng::Error::NoEntropy) => continue,
+ res => return res,
+ }
+ }
+}
+
pub fn random_array<const N: usize>() -> Result<[u8; N]> {
let mut arr = [0; N];
fill_with_entropy(&mut arr)?;
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index d217c00..78500af 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -36,6 +36,20 @@
out: ["avf_debug_policy_without_console_output.dtbo"],
}
+genrule {
+ name: "test_avf_debug_policy_with_adb",
+ defaults: ["test_avf_debug_policy_overlay"],
+ srcs: ["assets/avf_debug_policy_with_adb.dts"],
+ out: ["avf_debug_policy_with_adb.dtbo"],
+}
+
+genrule {
+ name: "test_avf_debug_policy_without_adb",
+ defaults: ["test_avf_debug_policy_overlay"],
+ srcs: ["assets/avf_debug_policy_without_adb.dts"],
+ out: ["avf_debug_policy_without_adb.dtbo"],
+}
+
java_test_host {
name: "MicrodroidHostTestCases",
srcs: ["java/**/*.java"],
@@ -64,6 +78,8 @@
":test_avf_debug_policy_without_ramdump",
":test_avf_debug_policy_with_console_output",
":test_avf_debug_policy_without_console_output",
+ ":test_avf_debug_policy_with_adb",
+ ":test_avf_debug_policy_without_adb",
"assets/bcc.dat",
],
data_native_bins: [
diff --git a/tests/hostside/assets/avf_debug_policy_with_adb.dts b/tests/hostside/assets/avf_debug_policy_with_adb.dts
new file mode 100644
index 0000000..9ad15dd
--- /dev/null
+++ b/tests/hostside/assets/avf_debug_policy_with_adb.dts
@@ -0,0 +1,18 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ fragment@avf {
+ target-path = "/";
+
+ __overlay__ {
+ avf {
+ guest {
+ microdroid {
+ adb = <1>;
+ };
+ };
+ };
+ };
+ };
+};
\ No newline at end of file
diff --git a/tests/hostside/assets/avf_debug_policy_with_ramdump.dts b/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
index f1a5196..26db7be 100644
--- a/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
+++ b/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
@@ -11,6 +11,9 @@
common {
ramdump = <1>;
};
+ microdroid {
+ adb = <1>; // adb is required to check VM's bootargs.
+ };
};
};
};
diff --git a/tests/hostside/assets/avf_debug_policy_without_adb.dts b/tests/hostside/assets/avf_debug_policy_without_adb.dts
new file mode 100644
index 0000000..992e0ff
--- /dev/null
+++ b/tests/hostside/assets/avf_debug_policy_without_adb.dts
@@ -0,0 +1,18 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ fragment@avf {
+ target-path = "/";
+
+ __overlay__ {
+ avf {
+ guest {
+ microdroid {
+ adb = <0>;
+ };
+ };
+ };
+ };
+ };
+};
\ No newline at end of file
diff --git a/tests/hostside/assets/avf_debug_policy_without_ramdump.dts b/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
index 5b15d51..194e314 100644
--- a/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
+++ b/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
@@ -11,6 +11,9 @@
common {
ramdump = <0>;
};
+ microdroid {
+ adb = <1>; // adb is required to check VM's bootargs.
+ };
};
};
};
diff --git a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
index 755613a..10f7003 100644
--- a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
@@ -34,6 +34,7 @@
import com.android.tradefed.device.TestDevice;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.FileUtil;
@@ -55,9 +56,11 @@
@NonNull private static final String PACKAGE_FILE_NAME = "MicrodroidTestApp.apk";
@NonNull private static final String PACKAGE_NAME = "com.android.microdroid.test";
@NonNull private static final String MICRODROID_DEBUG_FULL = "full";
+ @NonNull private static final String MICRODROID_DEBUG_NONE = "none";
@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";
private static final int BOOT_COMPLETE_TIMEOUT_MS = 30000; // 30 seconds
+ private static final int BOOT_FAILURE_WAIT_TIME_MS = 10000; // 10 seconds
private static final int CONSOLE_OUTPUT_WAIT_MS = 5000; // 5 seconds
@NonNull private static final String CUSTOM_PVMFW_FILE_PREFIX = "pvmfw";
@@ -146,7 +149,7 @@
public void testRamdump() throws Exception {
Pvmfw pvmfw = createPvmfw("avf_debug_policy_with_ramdump.dtbo");
pvmfw.serialize(mCustomPvmfwBinFileOnHost);
- mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted();
+ mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH)).contains("crashkernel=");
assertThat(readMicrodroidFileAsString(MICRODROID_DT_BOOTARGS_PATH))
@@ -159,7 +162,7 @@
public void testNoRamdump() throws Exception {
Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_ramdump.dtbo");
pvmfw.serialize(mCustomPvmfwBinFileOnHost);
- mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted();
+ mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH))
.doesNotContain("crashkernel=");
@@ -193,6 +196,33 @@
.isFalse();
}
+ @Test
+ public void testNoAdb_boots() throws Exception {
+ Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_adb.dtbo");
+ pvmfw.serialize(mCustomPvmfwBinFileOnHost);
+
+ // VM would boot, but cannot verify directly because of no adbd in the VM.
+ CommandResult result = tryLaunchProtectedNonDebuggableVm();
+ assertThat(result.getStatus()).isEqualTo(CommandStatus.TIMED_OUT);
+ assertWithMessage("Microdroid should have booted")
+ .that(result.getStderr())
+ .contains("payload is ready");
+ }
+
+ @Test
+ public void testNoAdb_noConnection() throws Exception {
+ Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_adb.dtbo");
+ pvmfw.serialize(mCustomPvmfwBinFileOnHost);
+
+ try {
+ launchProtectedVmAndWaitForBootCompleted(
+ MICRODROID_DEBUG_NONE, BOOT_FAILURE_WAIT_TIME_MS);
+ assertWithMessage("adb shouldn't be available").fail();
+ } catch (Exception e) {
+ // expected exception. passthrough.
+ }
+ }
+
@NonNull
private String readMicrodroidFileAsString(@NonNull String path)
throws DeviceNotAvailableException {
@@ -220,14 +250,20 @@
return result.getStdout().contains("Run /init as init process");
}
- private ITestDevice launchProtectedVmAndWaitForBootCompleted()
+ private ITestDevice launchProtectedVmAndWaitForBootCompleted(String debugLevel)
throws DeviceNotAvailableException {
+ return launchProtectedVmAndWaitForBootCompleted(debugLevel, BOOT_COMPLETE_TIMEOUT_MS);
+ }
+
+ private ITestDevice launchProtectedVmAndWaitForBootCompleted(
+ String debugLevel, long adbTimeoutMs) throws DeviceNotAvailableException {
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(
getPathForPackage(PACKAGE_NAME), MICRODROID_CONFIG_PATH)
- .debugLevel(MICRODROID_DEBUG_FULL)
+ .debugLevel(debugLevel)
.protectedVm(/* protectedVm= */ true)
.addBootFile(mCustomPvmfwBinFileOnHost, PVMFW_FILE_NAME)
+ .setAdbConnectTimeoutMs(adbTimeoutMs)
.build(mAndroidDevice);
assertThat(mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT_MS)).isTrue();
assertThat(mMicrodroidDevice.enableAdbRoot()).isTrue();
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 75dc50b..542f595 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -1899,6 +1899,7 @@
private static final int MS_NOEXEC = 8;
@Test
+ @CddTest(requirements = {"9.17/C-1-5"})
public void dataIsMountedWithNoExec() throws Exception {
assumeSupportedDevice();
@@ -1923,6 +1924,33 @@
.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);
diff --git a/virtualizationmanager/src/debug_config.rs b/virtualizationmanager/src/debug_config.rs
index a4ec419..666c98d 100644
--- a/virtualizationmanager/src/debug_config.rs
+++ b/virtualizationmanager/src/debug_config.rs
@@ -34,25 +34,26 @@
pub fn should_prepare_console_output(debug_level: DebugLevel) -> bool {
debug_level != DebugLevel::NONE
|| get_debug_policy_bool("/proc/device-tree/avf/guest/common/log").unwrap_or_default()
+ || get_debug_policy_bool("/proc/device-tree/avf/guest/microdroid/adb").unwrap_or_default()
+}
+
+/// Get whether debug apexes (MICRODROID_REQUIRED_APEXES_DEBUG) are required.
+pub fn should_include_debug_apexes(debug_level: DebugLevel) -> bool {
+ debug_level != DebugLevel::NONE
+ || get_debug_policy_bool("/proc/device-tree/avf/guest/microdroid/adb").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) => {
+ let debuggable = match config {
+ VirtualMachineConfig::RawConfig(_) => {
// custom VMs are considered debuggable for flexibility
- (config.protectedVm, true)
+ true
}
- VirtualMachineConfig::AppConfig(config) => {
- (config.protectedVm, config.debugLevel == DebugLevel::FULL)
- }
+ VirtualMachineConfig::AppConfig(config) => config.debugLevel == DebugLevel::FULL,
};
- if protected {
- enabled_in_dp
- } else {
- enabled_in_dp || debuggable
- }
+ enabled_in_dp || debuggable
}
diff --git a/virtualizationmanager/src/payload.rs b/virtualizationmanager/src/payload.rs
index 02e8f8e..99aea01 100644
--- a/virtualizationmanager/src/payload.rs
+++ b/virtualizationmanager/src/payload.rs
@@ -14,6 +14,7 @@
//! Payload disk image
+use crate::debug_config::should_include_debug_apexes;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
DiskImage::DiskImage,
Partition::Partition,
@@ -382,7 +383,7 @@
debug_level: DebugLevel,
) -> Vec<&'a ApexInfo> {
let mut additional_apexes: Vec<&str> = MICRODROID_REQUIRED_APEXES.to_vec();
- if debug_level != DebugLevel::NONE {
+ if should_include_debug_apexes(debug_level) {
additional_apexes.extend(MICRODROID_REQUIRED_APEXES_DEBUG.to_vec());
}
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/vmclient/src/lib.rs b/vmclient/src/lib.rs
index 0e3d140..d67d87e 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -57,7 +57,7 @@
"android.system.virtualizationservice";
const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
-const VIRTMGR_THREADS: usize = 16;
+const VIRTMGR_THREADS: usize = 2;
fn posix_pipe() -> Result<(OwnedFd, OwnedFd), io::Error> {
use nix::fcntl::OFlag;
@@ -122,7 +122,6 @@
let session = RpcSession::new();
session.set_file_descriptor_transport_mode(FileDescriptorTransportMode::Unix);
session.set_max_incoming_threads(VIRTMGR_THREADS);
- session.set_max_outgoing_threads(VIRTMGR_THREADS);
session
.setup_unix_domain_bootstrap_client(self.client_fd.as_fd())
.map_err(|_| io::Error::from(io::ErrorKind::ConnectionRefused))