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(&current_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))