Merge changes from topic "pvmfw_dt_validate"
* changes:
Embed the compiled template DT into pvmfw
Hardcode memory base address in platform.dts
Import platform.dts from u-boot
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 14452a3..3217ee1 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "MicrodroidHostTestCases"
},
{
- "name": "ComposHostTestCases"
- },
- {
"name": "MicrodroidTestApp"
},
{
@@ -36,6 +33,9 @@
"name": "ComposBenchmarkApp"
},
{
+ "name": "ComposHostTestCases"
+ },
+ {
"name": "AVFHostTestCases"
}
],
@@ -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/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 6659a57..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_setMaxOutgoingConnections(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/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/v3.rs b/libs/apkverify/src/v3.rs
index e1b728d..6082422 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -24,7 +24,7 @@
use openssl::x509::X509;
use std::fs::File;
use std::io::{Read, Seek};
-use std::ops::Range;
+use std::ops::RangeInclusive;
use std::path::Path;
use crate::algorithms::SignatureAlgorithmID;
@@ -33,11 +33,9 @@
pub const APK_SIGNATURE_SCHEME_V3_BLOCK_ID: u32 = 0xf05368c0;
-// TODO(b/190343842): get "ro.build.version.sdk"
-const SDK_INT: u32 = 31;
-
type Signers = LengthPrefixed<Vec<LengthPrefixed<Signer>>>;
+#[derive(Debug)]
pub(crate) struct Signer {
signed_data: LengthPrefixed<Bytes>, // not verified yet
min_sdk: u32,
@@ -47,8 +45,8 @@
}
impl Signer {
- fn sdk_range(&self) -> Range<u32> {
- self.min_sdk..self.max_sdk
+ fn sdk_range(&self) -> RangeInclusive<u32> {
+ self.min_sdk..=self.max_sdk
}
}
@@ -62,8 +60,8 @@
}
impl SignedData {
- fn sdk_range(&self) -> Range<u32> {
- self.min_sdk..self.max_sdk
+ fn sdk_range(&self) -> RangeInclusive<u32> {
+ self.min_sdk..=self.max_sdk
}
fn find_digest_by_algorithm(&self, algorithm_id: SignatureAlgorithmID) -> Result<&Digest> {
@@ -92,32 +90,30 @@
/// Verifies APK Signature Scheme v3 signatures of the provided APK and returns the public key
/// associated with the signer in DER format.
-pub fn verify<P: AsRef<Path>>(apk_path: P) -> Result<Box<[u8]>> {
+pub fn verify<P: AsRef<Path>>(apk_path: P, current_sdk: u32) -> Result<Box<[u8]>> {
let apk = File::open(apk_path.as_ref())?;
- let (signer, mut sections) = extract_signer_and_apk_sections(apk)?;
+ let (signer, mut sections) = extract_signer_and_apk_sections(apk, current_sdk)?;
signer.verify(&mut sections)
}
/// Gets the public key (in DER format) that was used to sign the given APK/APEX file
-pub fn get_public_key_der<P: AsRef<Path>>(apk_path: P) -> Result<Box<[u8]>> {
+pub fn get_public_key_der<P: AsRef<Path>>(apk_path: P, current_sdk: u32) -> Result<Box<[u8]>> {
let apk = File::open(apk_path.as_ref())?;
- let (signer, _) = extract_signer_and_apk_sections(apk)?;
+ let (signer, _) = extract_signer_and_apk_sections(apk, current_sdk)?;
Ok(signer.public_key.public_key_to_der()?.into_boxed_slice())
}
pub(crate) fn extract_signer_and_apk_sections<R: Read + Seek>(
apk: R,
+ current_sdk: u32,
) -> Result<(Signer, ApkSections<R>)> {
let mut sections = ApkSections::new(apk)?;
let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID).context(
"Fallback to v2 when v3 block not found is not yet implemented.", // b/197052981
)?;
- let mut supported = block
- .read::<Signers>()?
- .into_inner()
- .into_iter()
- .filter(|s| s.sdk_range().contains(&SDK_INT))
- .collect::<Vec<_>>();
+ let signers = block.read::<Signers>()?.into_inner();
+ let mut supported =
+ signers.into_iter().filter(|s| s.sdk_range().contains(¤t_sdk)).collect::<Vec<_>>();
ensure!(
supported.len() == 1,
"APK Signature Scheme V3 only supports one signer: {} signers found.",
diff --git a/libs/apkverify/src/v4.rs b/libs/apkverify/src/v4.rs
index 94abf99..045f4af 100644
--- a/libs/apkverify/src/v4.rs
+++ b/libs/apkverify/src/v4.rs
@@ -37,9 +37,10 @@
/// [apk_digest]: https://source.android.com/docs/security/apksigning/v4#apk-digest
pub fn get_apk_digest<R: Read + Seek>(
apk: R,
+ current_sdk: u32,
verify: bool,
) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
- let (signer, mut sections) = extract_signer_and_apk_sections(apk)?;
+ let (signer, mut sections) = extract_signer_and_apk_sections(apk, current_sdk)?;
let strongest_algorithm_id = signer
.strongest_signature()?
.signature_algorithm_id
@@ -104,9 +105,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 +119,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> {
@@ -153,6 +149,7 @@
/// function OOMing.
pub fn create(
mut apk: &mut R,
+ current_sdk: u32,
block_size: usize,
salt: &[u8],
algorithm: HashAlgorithm,
@@ -180,7 +177,8 @@
ret.hashing_info.log2_blocksize = log2(block_size);
apk.seek(SeekFrom::Start(start))?;
- let (signature_algorithm_id, apk_digest) = get_apk_digest(apk, /*verify=*/ false)?;
+ let (signature_algorithm_id, apk_digest) =
+ get_apk_digest(apk, current_sdk, /*verify=*/ false)?;
ret.signing_info.signature_algorithm_id = signature_algorithm_id;
ret.signing_info.apk_digest = apk_digest;
// TODO(jiyong): add a signature to the signing_info struct
@@ -367,8 +365,9 @@
#[test]
fn digest_from_apk() {
let mut input = Cursor::new(include_bytes!("../tests/data/v4-digest-v3-Sha256withEC.apk"));
+ let current_sdk = 31;
let mut created =
- V4Signature::create(&mut input, 4096, &[], HashAlgorithm::SHA256).unwrap();
+ V4Signature::create(&mut input, current_sdk, 4096, &[], HashAlgorithm::SHA256).unwrap();
let mut golden = V4Signature::from_idsig_path(format!("{}.idsig", TEST_APK_PATH)).unwrap();
diff --git a/libs/apkverify/tests/apkverify_test.rs b/libs/apkverify/tests/apkverify_test.rs
index baf7c42..f08b357 100644
--- a/libs/apkverify/tests/apkverify_test.rs
+++ b/libs/apkverify/tests/apkverify_test.rs
@@ -23,10 +23,12 @@
const KEY_NAMES_ECDSA: &[&str] = &["p256", "p384", "p521"];
const KEY_NAMES_RSA: &[&str] = &["1024", "2048", "3072", "4096", "8192", "16384"];
+const SDK_INT: u32 = 31;
+
#[test]
fn test_verify_truncated_cd() {
use zip::result::ZipError;
- let res = verify("tests/data/v2-only-truncated-cd.apk");
+ let res = verify("tests/data/v2-only-truncated-cd.apk", SDK_INT);
// TODO(b/190343842): consider making a helper for err assertion
assert!(matches!(
res.unwrap_err().root_cause().downcast_ref::<ZipError>().unwrap(),
@@ -42,7 +44,7 @@
#[test]
fn apks_signed_with_v3_dsa_sha256_are_not_supported() {
for key_name in KEY_NAMES_DSA.iter() {
- let res = verify(format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name));
+ let res = verify(format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name), SDK_INT);
assert!(res.is_err(), "DSA algorithm is not supported for verification. See b/197052981.");
assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
}
@@ -95,7 +97,7 @@
"tests/data/v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk",
];
for path in path_list.iter() {
- let res = verify(path);
+ let res = verify(path, SDK_INT);
assert!(res.is_err());
assert_contains(&res.unwrap_err().to_string(), "Signature is invalid");
}
@@ -103,22 +105,25 @@
#[test]
fn test_verify_v3_digest_mismatch() {
- let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk");
+ let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk", SDK_INT);
assert!(res.is_err());
assert_contains(&res.unwrap_err().to_string(), "Digest mismatch");
}
#[test]
fn test_verify_v3_wrong_apk_sig_block_magic() {
- let res = verify("tests/data/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk");
+ let res =
+ verify("tests/data/v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", SDK_INT);
assert!(res.is_err());
assert_contains(&res.unwrap_err().to_string(), "No APK Signing Block");
}
#[test]
fn test_verify_v3_apk_sig_block_size_mismatch() {
- let res =
- verify("tests/data/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk");
+ let res = verify(
+ "tests/data/v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk",
+ SDK_INT,
+ );
assert!(res.is_err());
assert_contains(
&res.unwrap_err().to_string(),
@@ -128,35 +133,35 @@
#[test]
fn test_verify_v3_cert_and_public_key_mismatch() {
- let res = verify("tests/data/v3-only-cert-and-public-key-mismatch.apk");
+ let res = verify("tests/data/v3-only-cert-and-public-key-mismatch.apk", SDK_INT);
assert!(res.is_err());
assert_contains(&res.unwrap_err().to_string(), "Public key mismatch");
}
#[test]
fn test_verify_v3_empty() {
- let res = verify("tests/data/v3-only-empty.apk");
+ let res = verify("tests/data/v3-only-empty.apk", SDK_INT);
assert!(res.is_err());
assert_contains(&res.unwrap_err().to_string(), "APK too small for APK Signing Block");
}
#[test]
fn test_verify_v3_no_certs_in_sig() {
- let res = verify("tests/data/v3-only-no-certs-in-sig.apk");
+ let res = verify("tests/data/v3-only-no-certs-in-sig.apk", SDK_INT);
assert!(res.is_err());
assert_contains(&res.unwrap_err().to_string(), "No certificates listed");
}
#[test]
fn test_verify_v3_no_supported_sig_algs() {
- let res = verify("tests/data/v3-only-no-supported-sig-algs.apk");
+ let res = verify("tests/data/v3-only-no-supported-sig-algs.apk", SDK_INT);
assert!(res.is_err());
assert_contains(&res.unwrap_err().to_string(), "No supported APK signatures found");
}
#[test]
fn test_verify_v3_signatures_and_digests_block_mismatch() {
- let res = verify("tests/data/v3-only-signatures-and-digests-block-mismatch.apk");
+ let res = verify("tests/data/v3-only-signatures-and-digests-block-mismatch.apk", SDK_INT);
assert!(res.is_err());
assert_contains(
&res.unwrap_err().to_string(),
@@ -203,14 +208,14 @@
/// * public key extracted from apk without verification
/// * expected public key from the corresponding .der file
fn validate_apk_public_key<P: AsRef<Path>>(apk_path: P) {
- let public_key_from_verification = verify(&apk_path);
+ let public_key_from_verification = verify(&apk_path, SDK_INT);
let public_key_from_verification =
public_key_from_verification.expect("Error in verification result");
let expected_public_key_path = format!("{}.der", apk_path.as_ref().to_str().unwrap());
assert_bytes_eq_to_data_in_file(&public_key_from_verification, expected_public_key_path);
- let public_key_from_apk = get_public_key_der(&apk_path);
+ let public_key_from_apk = get_public_key_der(&apk_path, SDK_INT);
let public_key_from_apk =
public_key_from_apk.expect("Error when extracting public key from apk");
assert_eq!(
@@ -226,15 +231,17 @@
fn validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
let apk = fs::File::open(&apk_path).expect("Unabled to open apk file");
- let (verified_algorithm_id, verified_digest) = get_apk_digest(&apk, /*verify=*/ true)
- .expect("Error when extracting apk digest with verification.");
+ let (verified_algorithm_id, verified_digest) =
+ get_apk_digest(&apk, SDK_INT, /*verify=*/ true)
+ .expect("Error when extracting apk digest with verification.");
assert_eq!(expected_algorithm_id, verified_algorithm_id);
let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
assert_bytes_eq_to_data_in_file(&verified_digest, expected_digest_path);
- let (unverified_algorithm_id, unverified_digest) = get_apk_digest(&apk, /*verify=*/ false)
- .expect("Error when extracting apk digest without verification.");
+ let (unverified_algorithm_id, unverified_digest) =
+ get_apk_digest(&apk, SDK_INT, /*verify=*/ false)
+ .expect("Error when extracting apk digest without verification.");
assert_eq!(expected_algorithm_id, unverified_algorithm_id);
assert_eq!(verified_digest, unverified_digest);
}
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/src/main.rs b/microdroid_manager/src/main.rs
index f83753c..fa96bf4 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -754,16 +754,23 @@
}
fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
+ let current_sdk = get_current_sdk()?;
if !root_hash_trustful {
- verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
+ verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
"failed to verify {}",
apk
)))
} else {
- get_public_key_der(apk)
+ get_public_key_der(apk, current_sdk)
}
}
+fn get_current_sdk() -> Result<u32> {
+ let current_sdk = system_properties::read("ro.build.version.sdk")?;
+ let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
+ current_sdk.parse().context("Malformed SDK version")
+}
+
fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
match payload_metadata {
PayloadMetadata::config_path(path) => {
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/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
index 8a63578..23f8ca6 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
@@ -27,7 +27,6 @@
}
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";
@@ -53,11 +52,6 @@
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 bff16a2..744f94c 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
@@ -60,10 +60,6 @@
return getDeviceProperties().isCuttlefish();
}
- public static boolean isGs101() {
- return getDeviceProperties().isGs101();
- }
-
public static boolean isUserBuild() {
return getDeviceProperties().isUserBuild();
}
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 6e4694c..542f595 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -2025,11 +2025,5 @@
.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/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 48e2431..e015d9d 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -48,7 +48,7 @@
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
BnVirtualMachineService, IVirtualMachineService,
};
-use anyhow::{bail, Context, Result};
+use anyhow::{anyhow, bail, Context, Result};
use apkverify::{HashAlgorithm, V4Signature};
use binder::{
self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor,
@@ -60,6 +60,7 @@
use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
use nix::unistd::pipe;
use rpcbinder::RpcServer;
+use rustutils::system_properties;
use semver::VersionReq;
use std::convert::TryInto;
use std::ffi::CStr;
@@ -111,8 +112,9 @@
if !metadata.is_file() {
bail!("input is not a regular file");
}
- let mut sig = V4Signature::create(&mut input, 4096, &[], HashAlgorithm::SHA256)
- .context("failed to create idsig")?;
+ let mut sig =
+ V4Signature::create(&mut input, get_current_sdk()?, 4096, &[], HashAlgorithm::SHA256)
+ .context("failed to create idsig")?;
let mut output = clone_file(idsig_fd)?;
output.set_len(0).context("failed to set_len on the idsig output")?;
@@ -120,6 +122,12 @@
Ok(())
}
+fn get_current_sdk() -> Result<u32> {
+ let current_sdk = system_properties::read("ro.build.version.sdk")?;
+ let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
+ current_sdk.parse().context("Malformed SDK version")
+}
+
pub fn remove_temporary_files(path: &PathBuf) -> Result<()> {
for dir_entry in read_dir(path)? {
remove_file(dir_entry?.path())?;
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/vmclient/src/lib.rs b/vmclient/src/lib.rs
index 77ddf05..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_connections(VIRTMGR_THREADS);
session
.setup_unix_domain_bootstrap_client(self.client_fd.as_fd())
.map_err(|_| io::Error::from(io::ErrorKind::ConnectionRefused))