Merge "Add a README for vm_payload"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 43c89d4..45519d4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -19,4 +19,3 @@
 
 [Hook Scripts]
 aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
-checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/apex/Android.bp b/apex/Android.bp
index 596493a..579d7c7 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -4,8 +4,7 @@
 
 microdroid_filesystem_images = [
     "microdroid_boot",
-    "microdroid_bootconfig_app_debuggable",
-    "microdroid_bootconfig_full_debuggable",
+    "microdroid_bootconfig_debuggable",
     "microdroid_bootconfig_normal",
     "microdroid_init_boot",
     "microdroid_super",
@@ -93,8 +92,7 @@
     prebuilts: [
         "com.android.virt.init.rc",
         "features_com.android.virt.xml",
-        "microdroid_initrd_app_debuggable",
-        "microdroid_initrd_full_debuggable",
+        "microdroid_initrd_debuggable",
         "microdroid_initrd_normal",
         "microdroid.json",
         "microdroid_bootloader",
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index c8cb07d..4d83c5f 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -352,8 +352,7 @@
     'vbmeta.img': 'etc/fs/microdroid_vbmeta.img',
     'vbmeta_bootconfig.img': 'etc/fs/microdroid_vbmeta_bootconfig.img',
     'bootconfig.normal': 'etc/fs/microdroid_bootconfig.normal',
-    'bootconfig.app_debuggable': 'etc/fs/microdroid_bootconfig.app_debuggable',
-    'bootconfig.full_debuggable': 'etc/fs/microdroid_bootconfig.full_debuggable',
+    'bootconfig.debuggable': 'etc/fs/microdroid_bootconfig.debuggable',
     'uboot_env.img': 'etc/fs/uboot_env.img'
 }
 
@@ -400,8 +399,7 @@
     # Re-sign bootconfigs and the uboot_env with the same key
     bootconfig_sign_key = key
     Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.normal'])
-    Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.app_debuggable'])
-    Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.full_debuggable'])
+    Async(AddHashFooter, args, bootconfig_sign_key, files['bootconfig.debuggable'])
     Async(AddHashFooter, args, bootconfig_sign_key, files['uboot_env.img'])
 
     # Re-sign vbmeta_bootconfig with chained_partitions to "bootconfig" and
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 601c6fc..34be1d5 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -97,11 +97,7 @@
             };
         let config_path = get_vm_config_path(has_system_ext, parameters.prefer_staged);
 
-        let debug_level = match (protected_vm, parameters.debug_mode) {
-            (_, true) => DebugLevel::FULL,
-            (false, false) => DebugLevel::APP_ONLY,
-            (true, false) => DebugLevel::NONE,
-        };
+        let debug_level = if parameters.debug_mode { DebugLevel::FULL } else { DebugLevel::NONE };
 
         let (console_fd, log_fd) = if debug_level == DebugLevel::NONE {
             (None, None)
diff --git a/compos/src/compilation.rs b/compos/src/compilation.rs
index 2872d95..10eca96 100644
--- a/compos/src/compilation.rs
+++ b/compos/src/compilation.rs
@@ -43,8 +43,8 @@
 fn validate_args(args: &OdrefreshArgs) -> Result<()> {
     if args.compilationMode != CompilationMode::NORMAL_COMPILE {
         // Conservatively check debuggability.
-        let debuggable = system_properties::read_bool("ro.boot.microdroid.app_debuggable", false)
-            .unwrap_or(false);
+        let debuggable =
+            system_properties::read_bool("ro.boot.microdroid.debuggable", false).unwrap_or(false);
         if !debuggable {
             bail!("Requested compilation mode only available in debuggable VMs");
         }
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index f364f4c..fb7c98c 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -63,8 +63,7 @@
     method @Nullable public String getPayloadBinaryPath();
     method public boolean isCompatibleWith(@NonNull android.system.virtualmachine.VirtualMachineConfig);
     method public boolean isProtectedVm();
-    field public static final int DEBUG_LEVEL_APP_ONLY = 1; // 0x1
-    field public static final int DEBUG_LEVEL_FULL = 2; // 0x2
+    field public static final int DEBUG_LEVEL_FULL = 1; // 0x1
     field public static final int DEBUG_LEVEL_NONE = 0; // 0x0
   }
 
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index f9f29a1..a9e062a 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -70,7 +70,6 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = "DEBUG_LEVEL_", value = {
             DEBUG_LEVEL_NONE,
-            DEBUG_LEVEL_APP_ONLY,
             DEBUG_LEVEL_FULL
     })
     public @interface DebugLevel {}
@@ -84,20 +83,12 @@
     @SystemApi public static final int DEBUG_LEVEL_NONE = 0;
 
     /**
-     * Only the app is debuggable. Log from the app is exported from the VM. Debugger can be
-     * attached to the app process. Rest of the VM is not debuggable.
-     *
-     * @hide
-     */
-    @SystemApi public static final int DEBUG_LEVEL_APP_ONLY = 1;
-
-    /**
      * Fully debuggable. All logs (both logcat and kernel message) are exported. All processes
      * running in the VM can be attached to the debugger. Rooting is possible.
      *
      * @hide
      */
-    @SystemApi public static final int DEBUG_LEVEL_FULL = 2;
+    @SystemApi public static final int DEBUG_LEVEL_FULL = 1;
 
     /** Absolute path to the APK file containing the VM payload. */
     @NonNull private final String mApkPath;
@@ -152,8 +143,7 @@
                     + "range [1, " + availableCpus + "]");
         }
 
-        if (debugLevel != DEBUG_LEVEL_NONE && debugLevel != DEBUG_LEVEL_APP_ONLY
-                && debugLevel != DEBUG_LEVEL_FULL) {
+        if (debugLevel != DEBUG_LEVEL_NONE && debugLevel != DEBUG_LEVEL_FULL) {
             throw new IllegalArgumentException("Invalid debugLevel: " + debugLevel);
         }
 
@@ -230,8 +220,7 @@
             }
         }
         @DebugLevel int debugLevel = b.getInt(KEY_DEBUGLEVEL);
-        if (debugLevel != DEBUG_LEVEL_NONE && debugLevel != DEBUG_LEVEL_APP_ONLY
-                && debugLevel != DEBUG_LEVEL_FULL) {
+        if (debugLevel != DEBUG_LEVEL_NONE && debugLevel != DEBUG_LEVEL_FULL) {
             throw new VirtualMachineException("Invalid debugLevel: " + debugLevel);
         }
         boolean protectedVm = b.getBoolean(KEY_PROTECTED_VM);
@@ -384,9 +373,6 @@
                     VirtualMachineAppConfig.Payload.configPath(mPayloadConfigPath);
         }
         switch (mDebugLevel) {
-            case DEBUG_LEVEL_APP_ONLY:
-                vsConfig.debugLevel = VirtualMachineAppConfig.DebugLevel.APP_ONLY;
-                break;
             case DEBUG_LEVEL_FULL:
                 vsConfig.debugLevel = VirtualMachineAppConfig.DebugLevel.FULL;
                 break;
diff --git a/launcher/Android.bp b/launcher/Android.bp
index 123ec2e..6c6417f 100644
--- a/launcher/Android.bp
+++ b/launcher/Android.bp
@@ -6,8 +6,10 @@
     name: "microdroid_launcher",
     srcs: ["main.cpp"],
     shared_libs: [
+        "libbase",
         "libdl",
         "libdl_android",
+        "liblog",
     ],
     header_libs: ["vm_payload_headers"],
 }
diff --git a/launcher/main.cpp b/launcher/main.cpp
index ae55be9..c3f9988 100644
--- a/launcher/main.cpp
+++ b/launcher/main.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <android-base/logging.h>
+#include <android-base/result.h>
 #include <android/dlext.h>
 #include <dlfcn.h>
 
@@ -23,6 +25,9 @@
 
 #include "vm_main.h"
 
+using android::base::Error;
+using android::base::Result;
+
 extern "C" {
 enum {
     ANDROID_NAMESPACE_TYPE_REGULAR = 0,
@@ -40,7 +45,7 @@
                                     const char* shared_libs_sonames);
 } // extern "C"
 
-static void* load(const std::string& libname);
+static Result<void*> load(const std::string& libname);
 
 constexpr char entrypoint_name[] = "AVmPayload_main";
 
@@ -56,16 +61,18 @@
         return EXIT_FAILURE;
     }
 
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
     const char* libname = argv[1];
-    void* handle = load(libname);
-    if (handle == nullptr) {
-        std::cerr << "Failed to load " << libname << ": " << dlerror() << "\n";
+    auto handle = load(libname);
+    if (!handle.ok()) {
+        LOG(ERROR) << "Failed to load " << libname << ": " << handle.error().message();
         return EXIT_FAILURE;
     }
 
-    AVmPayload_main_t* entry = reinterpret_cast<decltype(entry)>(dlsym(handle, entrypoint_name));
+    AVmPayload_main_t* entry = reinterpret_cast<decltype(entry)>(dlsym(*handle, entrypoint_name));
     if (entry == nullptr) {
-        std::cerr << "Failed to find entrypoint `" << entrypoint_name << "`: " << dlerror() << "\n";
+        LOG(ERROR) << "Failed to find entrypoint `" << entrypoint_name << "`: " << dlerror();
         return EXIT_FAILURE;
     }
 
@@ -75,7 +82,7 @@
 // Create a new linker namespace whose search path is set to the directory of the library. Then
 // load it from there. Returns the handle to the loaded library if successful. Returns nullptr
 // if failed.
-void* load(const std::string& libname) {
+Result<void*> load(const std::string& libname) {
     // Parent as nullptr means the default namespace
     android_namespace_t* parent = nullptr;
     // The search paths of the new namespace are isolated to restrict system private libraries.
@@ -89,8 +96,7 @@
     new_ns = android_create_namespace("microdroid_app", ld_library_path, default_library_path, type,
                                       /* permitted_when_isolated_path */ nullptr, parent);
     if (new_ns == nullptr) {
-        std::cerr << "Failed to create linker namespace: " << dlerror() << "\n";
-        return nullptr;
+        return Error() << "Failed to create linker namespace: " << dlerror();
     }
 
     std::string libs;
@@ -98,11 +104,17 @@
         if (!libs.empty()) libs += ':';
         libs += lib;
     }
-    android_link_namespaces(new_ns, nullptr, libs.c_str());
+    if (!android_link_namespaces(new_ns, nullptr, libs.c_str())) {
+        return Error() << "Failed to link namespace: " << dlerror();
+    }
 
     const android_dlextinfo info = {
             .flags = ANDROID_DLEXT_USE_NAMESPACE,
             .library_namespace = new_ns,
     };
-    return android_dlopen_ext(libname.c_str(), RTLD_NOW, &info);
+    if (auto ret = android_dlopen_ext(libname.c_str(), RTLD_NOW, &info); ret) {
+        return ret;
+    } else {
+        return Error() << "Failed to dlopen: " << dlerror();
+    }
 }
diff --git a/libs/dice/src/bcc.rs b/libs/dice/src/bcc.rs
new file mode 100644
index 0000000..849dfa0
--- /dev/null
+++ b/libs/dice/src/bcc.rs
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+//! Wrapper around dice/android/bcc.h.
+
+use core::mem;
+use core::ptr;
+
+use open_dice_bcc_bindgen::BccHandoverParse;
+
+use crate::check_call;
+use crate::Cdi;
+use crate::Error;
+use crate::Result;
+
+/// Boot Chain Certificate handover format combining the BCC and CDIs in a single CBOR object.
+#[derive(Clone, Debug)]
+pub struct Handover<'a> {
+    /// Attestation CDI.
+    pub cdi_attest: &'a Cdi,
+    /// Sealing CDI.
+    pub cdi_seal: &'a Cdi,
+    /// Boot Chain Certificate (optional).
+    pub bcc: Option<&'a [u8]>,
+}
+
+impl<'a> Handover<'a> {
+    /// Validates and extracts the fields of a BCC handover buffer.
+    pub fn new(buffer: &'a [u8]) -> Result<Self> {
+        let mut cdi_attest: *const u8 = ptr::null();
+        let mut cdi_seal: *const u8 = ptr::null();
+        let mut bcc: *const u8 = ptr::null();
+        let mut bcc_size: usize = 0;
+
+        // SAFETY - The buffer is only read and never stored and the returned pointers should all
+        // point within the address range of the buffer or be NULL.
+        check_call(unsafe {
+            BccHandoverParse(
+                buffer.as_ptr(),
+                buffer.len(),
+                &mut cdi_attest as *mut *const u8,
+                &mut cdi_seal as *mut *const u8,
+                &mut bcc as *mut *const u8,
+                &mut bcc_size as *mut usize,
+            )
+        })?;
+
+        let cdi_attest = {
+            let i = index_from_ptr(buffer, cdi_attest).ok_or(Error::PlatformError)?;
+            let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(Error::PlatformError)?;
+            s.try_into().map_err(|_| Error::PlatformError)?
+        };
+        let cdi_seal = {
+            let i = index_from_ptr(buffer, cdi_seal).ok_or(Error::PlatformError)?;
+            let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(Error::PlatformError)?;
+            s.try_into().map_err(|_| Error::PlatformError)?
+        };
+        let bcc = if bcc.is_null() {
+            None
+        } else {
+            let i = index_from_ptr(buffer, bcc).ok_or(Error::PlatformError)?;
+            Some(buffer.get(i..(i + bcc_size)).ok_or(Error::PlatformError)?)
+        };
+
+        Ok(Self { cdi_attest, cdi_seal, bcc })
+    }
+}
+
+fn index_from_ptr(slice: &[u8], pointer: *const u8) -> Option<usize> {
+    if slice.as_ptr_range().contains(&pointer) {
+        (pointer as usize).checked_sub(slice.as_ptr() as usize)
+    } else {
+        None
+    }
+}
diff --git a/libs/dice/src/lib.rs b/libs/dice/src/lib.rs
index 9e39436..43d167f 100644
--- a/libs/dice/src/lib.rs
+++ b/libs/dice/src/lib.rs
@@ -18,16 +18,23 @@
 
 #![no_std]
 
-use core::fmt::{self, Debug};
-use open_dice_cbor_bindgen::{
-    DiceHash, DiceResult, DiceResult_kDiceResultBufferTooSmall as DICE_RESULT_BUFFER_TOO_SMALL,
-    DiceResult_kDiceResultInvalidInput as DICE_RESULT_INVALID_INPUT,
-    DiceResult_kDiceResultOk as DICE_RESULT_OK,
-    DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR,
-};
+use core::fmt;
+use core::result;
 
+use open_dice_cbor_bindgen::DiceHash;
+use open_dice_cbor_bindgen::DiceResult;
+use open_dice_cbor_bindgen::DiceResult_kDiceResultBufferTooSmall as DICE_RESULT_BUFFER_TOO_SMALL;
+use open_dice_cbor_bindgen::DiceResult_kDiceResultInvalidInput as DICE_RESULT_INVALID_INPUT;
+use open_dice_cbor_bindgen::DiceResult_kDiceResultOk as DICE_RESULT_OK;
+use open_dice_cbor_bindgen::DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR;
+
+pub mod bcc;
+
+const CDI_SIZE: usize = open_dice_cbor_bindgen::DICE_CDI_SIZE as usize;
 const HASH_SIZE: usize = open_dice_cbor_bindgen::DICE_HASH_SIZE as usize;
 
+/// Array type of CDIs.
+pub type Cdi = [u8; CDI_SIZE];
 /// Array type of hashes used by DICE.
 pub type Hash = [u8; HASH_SIZE];
 
@@ -43,7 +50,7 @@
     Unknown(DiceResult),
 }
 
-impl Debug for Error {
+impl fmt::Debug for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             Error::InvalidInput => write!(f, "invalid input"),
@@ -54,7 +61,10 @@
     }
 }
 
-fn check_call(ret: DiceResult) -> Result<(), Error> {
+/// Result of DICE functions.
+pub type Result<T> = result::Result<T, Error>;
+
+fn check_call(ret: DiceResult) -> Result<()> {
     match ret {
         DICE_RESULT_OK => Ok(()),
         DICE_RESULT_INVALID_INPUT => Err(Error::InvalidInput),
@@ -69,7 +79,7 @@
 }
 
 /// Hash the provided input using DICE's default hash function.
-pub fn hash(bytes: &[u8]) -> Result<Hash, Error> {
+pub fn hash(bytes: &[u8]) -> Result<Hash> {
     let mut output: Hash = [0; HASH_SIZE];
     // SAFETY - DiceHash takes a sized input buffer and writes to a constant-sized output buffer.
     check_call(unsafe { DiceHash(ctx(), bytes.as_ptr(), bytes.len(), output.as_mut_ptr()) })?;
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 79378fe..028ac1f 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -67,7 +67,7 @@
         "heapprofd_client_api",
         "libartpalette-system",
 
-        "apexd",
+        "apexd.microdroid",
         "atrace",
         "debuggerd",
         "linker",
@@ -373,18 +373,9 @@
 }
 
 avb_add_hash_footer {
-    name: "microdroid_bootconfig_app_debuggable",
-    src: "bootconfig.app_debuggable",
-    filename: "microdroid_bootconfig.app_debuggable",
-    partition_name: "bootconfig",
-    private_key: ":microdroid_sign_key",
-    salt: bootconfig_salt,
-}
-
-avb_add_hash_footer {
-    name: "microdroid_bootconfig_full_debuggable",
-    src: "bootconfig.full_debuggable",
-    filename: "microdroid_bootconfig.full_debuggable",
+    name: "microdroid_bootconfig_debuggable",
+    src: "bootconfig.debuggable",
+    filename: "microdroid_bootconfig.debuggable",
     partition_name: "bootconfig",
     private_key: ":microdroid_sign_key",
     salt: bootconfig_salt,
@@ -535,13 +526,8 @@
 }
 
 filegroup {
-    name: "microdroid_bootconfig_full_debuggable_src",
-    srcs: ["bootconfig.full_debuggable"],
-}
-
-filegroup {
-    name: "microdroid_bootconfig_app_debuggable_src",
-    srcs: ["bootconfig.app_debuggable"],
+    name: "microdroid_bootconfig_debuggable_src",
+    srcs: ["bootconfig.debuggable"],
 }
 
 filegroup {
diff --git a/microdroid/bootconfig.app_debuggable b/microdroid/bootconfig.app_debuggable
deleted file mode 100644
index 529ed07..0000000
--- a/microdroid/bootconfig.app_debuggable
+++ /dev/null
@@ -1,14 +0,0 @@
-# The app is debuggable.
-androidboot.microdroid.app_debuggable=1
-
-# TODO(b/203369076) This should be 0 to disable adb rooting. For now, we can't do that because
-# if this is set to 0, adbd enforces the host authentication but we don't put the adb
-# public key (which represents the owner) in the VM yet.
-androidboot.microdroid.debuggable=0
-
-# Console output is not redirect to the host-side.
-kernel.printk.devkmsg=off
-kernel.console=ttynull
-
-# ADB is supported but rooting is prohibited.
-androidboot.adb.enabled=1
diff --git a/microdroid/bootconfig.full_debuggable b/microdroid/bootconfig.debuggable
similarity index 76%
rename from microdroid/bootconfig.full_debuggable
rename to microdroid/bootconfig.debuggable
index 583060b..0d0457c 100644
--- a/microdroid/bootconfig.full_debuggable
+++ b/microdroid/bootconfig.debuggable
@@ -1,6 +1,3 @@
-# The app is debuggable as full_debuggable is a superser of app_debuggable.
-androidboot.microdroid.app_debuggable=1
-
 # ro.debuggable is set.
 androidboot.microdroid.debuggable=1
 
diff --git a/microdroid/bootconfig.normal b/microdroid/bootconfig.normal
index 5cc553c..9226ca1 100644
--- a/microdroid/bootconfig.normal
+++ b/microdroid/bootconfig.normal
@@ -1,6 +1,3 @@
-# The app is not debuggable.
-androidboot.microdroid.app_debuggable=0
-
 # ro.debuggable is off
 androidboot.microdroid.debuggable=0
 
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 310cf2b..a48ba4b 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -19,10 +19,10 @@
 
 # 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.app_debuggable=1
+on early-init && property:ro.boot.microdroid.debuggable=1
     setprop ro.log.file_logger.path /dev/hvc2
 
-on early-init && property:ro.boot.microdroid.app_debuggable=0
+on early-init && property:ro.boot.microdroid.debuggable=0
     setprop ro.log.file_logger.path /dev/null
 
 on init
diff --git a/microdroid/initrd/Android.bp b/microdroid/initrd/Android.bp
index eb761bf..d05ea86 100644
--- a/microdroid/initrd/Android.bp
+++ b/microdroid/initrd/Android.bp
@@ -50,46 +50,24 @@
 ]
 
 genrule {
-    name: "microdroid_initrd_full_debuggable_arm64",
+    name: "microdroid_initrd_debuggable_arm64",
     tools: ["initrd_bootconfig"],
     srcs: [
         ":microdroid_initrd_gen",
-        ":microdroid_bootconfig_full_debuggable_src",
+        ":microdroid_bootconfig_debuggable_src",
     ] + bootconfigs_arm64,
-    out: ["microdroid_initrd_full_debuggable_arm64"],
+    out: ["microdroid_initrd_debuggable_arm64"],
     cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
 }
 
 genrule {
-    name: "microdroid_initrd_full_debuggable_x86_64",
+    name: "microdroid_initrd_debuggable_x86_64",
     tools: ["initrd_bootconfig"],
     srcs: [
         ":microdroid_initrd_gen",
-        ":microdroid_bootconfig_full_debuggable_src",
+        ":microdroid_bootconfig_debuggable_src",
     ] + bootconfigs_x86_64,
-    out: ["microdroid_initrd_full_debuggable_x86_64"],
-    cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
-}
-
-genrule {
-    name: "microdroid_initrd_app_debuggable_arm64",
-    tools: ["initrd_bootconfig"],
-    srcs: [
-        ":microdroid_initrd_gen",
-        ":microdroid_bootconfig_app_debuggable_src",
-    ] + bootconfigs_arm64,
-    out: ["microdroid_initrd_app_debuggable_arm64"],
-    cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
-}
-
-genrule {
-    name: "microdroid_initrd_app_debuggable_x86_64",
-    tools: ["initrd_bootconfig"],
-    srcs: [
-        ":microdroid_initrd_gen",
-        ":microdroid_bootconfig_app_debuggable_src",
-    ] + bootconfigs_x86_64,
-    out: ["microdroid_initrd_app_debuggable_x86_64"],
+    out: ["microdroid_initrd_debuggable_x86_64"],
     cmd: "$(location initrd_bootconfig) --output $(out) $(in)",
 }
 
@@ -116,33 +94,18 @@
 }
 
 prebuilt_etc {
-    name: "microdroid_initrd_full_debuggable",
+    name: "microdroid_initrd_debuggable",
     // We don't have ramdisk for architectures other than x86_64 & arm64
     src: "empty_file",
     arch: {
         x86_64: {
-            src: ":microdroid_initrd_full_debuggable_x86_64",
+            src: ":microdroid_initrd_debuggable_x86_64",
         },
         arm64: {
-            src: ":microdroid_initrd_full_debuggable_arm64",
+            src: ":microdroid_initrd_debuggable_arm64",
         },
     },
-    filename: "microdroid_initrd_full_debuggable.img",
-}
-
-prebuilt_etc {
-    name: "microdroid_initrd_app_debuggable",
-    // We don't have ramdisk for architectures other than x86_64 & arm64
-    src: "empty_file",
-    arch: {
-        x86_64: {
-            src: ":microdroid_initrd_app_debuggable_x86_64",
-        },
-        arm64: {
-            src: ":microdroid_initrd_app_debuggable_arm64",
-        },
-    },
-    filename: "microdroid_initrd_app_debuggable.img",
+    filename: "microdroid_initrd_debuggable.img",
 }
 
 prebuilt_etc {
@@ -168,15 +131,8 @@
 }
 
 genrule {
-    name: "microdroid_initrd_app_debuggable.sha256",
-    srcs: [":microdroid_initrd_app_debuggable"],
-    cmd: "cat $(in) | sha256sum | cut -d' ' -f1 > $(out)",
-    out: ["hash"],
-}
-
-genrule {
-    name: "microdroid_initrd_full_debuggable.sha256",
-    srcs: [":microdroid_initrd_full_debuggable"],
+    name: "microdroid_initrd_debuggable.sha256",
+    srcs: [":microdroid_initrd_debuggable"],
     cmd: "cat $(in) | sha256sum | cut -d' ' -f1 > $(out)",
     out: ["hash"],
 }
@@ -185,8 +141,7 @@
     name: "microdroid_initrd_hashes",
     srcs: [
         ":microdroid_initrd_normal.sha256",
-        ":microdroid_initrd_app_debuggable.sha256",
-        ":microdroid_initrd_full_debuggable.sha256",
+        ":microdroid_initrd_debuggable.sha256",
     ],
     // join the hashes with commas
     cmd: "cat $(in) | tr '\n' ',' > $(out) && truncate -s -1 $(out)",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 0e45461..3c490f4 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -76,7 +76,7 @@
     "/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
 
 const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
-const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
+const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
 const APK_MOUNT_DONE_PROP: &str = "microdroid_manager.apk.mounted";
 
 // SYNC WITH virtualizationservice/src/crosvm.rs
@@ -167,7 +167,7 @@
 
 fn main() -> Result<()> {
     // If debuggable, print full backtrace to console log with stdio_to_kmsg
-    if system_properties::read_bool(APP_DEBUGGABLE_PROP, true)? {
+    if system_properties::read_bool(DEBUGGABLE_PROP, true)? {
         env::set_var("RUST_BACKTRACE", "full");
     }
 
@@ -281,12 +281,12 @@
         }
     }
 
-    // Check app debuggability, conervatively assuming it is debuggable
-    let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
+    // Check debuggability, conservatively assuming it is debuggable
+    let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
 
     // Send the details to diced
     let hidden = verified_data.salt.clone().try_into().unwrap();
-    dice.derive(code_hash, &config_desc, authority_hash, app_debuggable, hidden)
+    dice.derive(code_hash, &config_desc, authority_hash, debuggable, hidden)
 }
 
 fn encode_tstr(tstr: &str, buffer: &mut Vec<u8>) -> Result<()> {
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 0da24c7..6a01713 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -15,6 +15,7 @@
         "libaarch64_paging",
         "libavb_nostd",
         "libbuddy_system_allocator",
+        "libdice_nostd",
         "liblibfdt",
         "liblog_rust_nostd",
         "libpvmfw_embedded_key",
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index e8f9bb2..bffc140 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -24,6 +24,7 @@
 use core::arch::asm;
 use core::num::NonZeroUsize;
 use core::slice;
+use dice::bcc::Handover;
 use log::debug;
 use log::error;
 use log::info;
@@ -228,8 +229,9 @@
         RebootReason::InvalidConfig
     })?;
 
-    let bcc = appended.get_bcc_mut().ok_or_else(|| {
-        error!("Invalid BCC");
+    let bcc_slice = appended.get_bcc_mut();
+    let bcc = Handover::new(bcc_slice).map_err(|e| {
+        error!("Invalid BCC Handover: {e:?}");
         RebootReason::InvalidBcc
     })?;
 
@@ -243,7 +245,7 @@
     let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
 
     // This wrapper allows main() to be blissfully ignorant of platform details.
-    crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc, &mut memory)?;
+    crate::main(slices.fdt, slices.kernel, slices.ramdisk, &bcc, &mut memory)?;
 
     // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
 
@@ -366,12 +368,10 @@
         }
     }
 
-    fn get_bcc_mut(&mut self) -> Option<&mut [u8]> {
-        let bcc = match self {
+    fn get_bcc_mut(&mut self) -> &mut [u8] {
+        match self {
             Self::LegacyBcc(ref mut bcc) => bcc,
             Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
-        };
-        // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
-        Some(bcc)
+        }
     }
 }
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index e6a158d..07cbd0c 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -39,14 +39,15 @@
 };
 use avb::PUBLIC_KEY;
 use avb_nostd::verify_image;
+use dice::bcc;
 use libfdt::Fdt;
-use log::{debug, error, info};
+use log::{debug, error, info, trace};
 
 fn main(
     fdt: &Fdt,
     signed_kernel: &[u8],
     ramdisk: Option<&[u8]>,
-    bcc: &[u8],
+    bcc: &bcc::Handover,
     memory: &mut MemoryTracker,
 ) -> Result<(), RebootReason> {
     info!("pVM firmware");
@@ -57,7 +58,7 @@
     } else {
         debug!("Ramdisk: None");
     }
-    debug!("BCC: {:?} ({:#x} bytes)", bcc.as_ptr(), bcc.len());
+    trace!("BCC: {bcc:x?}");
 
     // Set up PCI bus for VirtIO devices.
     let pci_node = pci_node(fdt)?;
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 bd5b180..72a0090 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
@@ -132,6 +132,8 @@
         private OptionalLong mKernelStartedNanoTime = OptionalLong.empty();
         private OptionalLong mInitStartedNanoTime = OptionalLong.empty();
         private OptionalLong mPayloadStartedNanoTime = OptionalLong.empty();
+        private StringBuilder mConsoleOutput = new StringBuilder();
+        private StringBuilder mLogOutput = new StringBuilder();
 
         private void processBootEvents(String log) {
             if (!mVcpuStartedNanoTime.isPresent()) {
@@ -149,44 +151,50 @@
             }
         }
 
-        private void logVmOutputAndMonitorBootEvents(String tag,
+        private void logVmOutputAndMonitorBootEvents(
+                String tag,
                 InputStream vmOutputStream,
                 String name,
+                StringBuilder result,
                 boolean monitorEvents) {
             new Thread(
-                    () -> {
-                        try {
-                            BufferedReader reader =
-                                    new BufferedReader(new InputStreamReader(vmOutputStream));
-                            String line;
-                            while ((line = reader.readLine()) != null
-                                    && !Thread.interrupted()) {
-                                if (monitorEvents) processBootEvents(line);
-                                Log.i(tag, name + ": " + line);
-                            }
-                        } catch (Exception e) {
-                            Log.w(tag, name, e);
-                        }
-                    }).start();
+                            () -> {
+                                try {
+                                    BufferedReader reader =
+                                            new BufferedReader(
+                                                    new InputStreamReader(vmOutputStream));
+                                    String line;
+                                    while ((line = reader.readLine()) != null
+                                            && !Thread.interrupted()) {
+                                        if (monitorEvents) processBootEvents(line);
+                                        Log.i(tag, name + ": " + line);
+                                        result.append(line + "\n");
+                                    }
+                                } catch (Exception e) {
+                                    Log.w(tag, name, e);
+                                }
+                            })
+                    .start();
         }
 
-        private void logVmOutputAndMonitorBootEvents(String tag,
-                InputStream vmOutputStream,
-                String name) {
-            logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, true);
+        private void logVmOutputAndMonitorBootEvents(
+                String tag, InputStream vmOutputStream, String name, StringBuilder result) {
+            logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, result, true);
         }
 
         /** Copy output from the VM to logcat. This is helpful when things go wrong. */
-        protected void logVmOutput(String tag, InputStream vmOutputStream, String name) {
-            logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, false);
+        protected void logVmOutput(
+                String tag, InputStream vmOutputStream, String name, StringBuilder result) {
+            logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, result, false);
         }
 
         public void runToFinish(String logTag, VirtualMachine vm)
                 throws VirtualMachineException, InterruptedException {
             vm.setCallback(mExecutorService, this);
             vm.run();
-            logVmOutputAndMonitorBootEvents(logTag, vm.getConsoleOutput(), "Console");
-            logVmOutput(logTag, vm.getLogOutput(), "Log");
+            logVmOutputAndMonitorBootEvents(
+                    logTag, vm.getConsoleOutput(), "Console", mConsoleOutput);
+            logVmOutput(logTag, vm.getLogOutput(), "Log", mLogOutput);
             mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
         }
 
@@ -206,6 +214,14 @@
             return mPayloadStartedNanoTime;
         }
 
+        public String getConsoleOutput() {
+            return mConsoleOutput.toString();
+        }
+
+        public String getLogOutput() {
+            return mLogOutput.toString();
+        }
+
         protected void forceStop(VirtualMachine vm) {
             try {
                 vm.stop();
@@ -248,14 +264,20 @@
         public final OptionalLong initStartedNanoTime;
         public final OptionalLong payloadStartedNanoTime;
 
-        BootResult(boolean payloadStarted,
+        public final String consoleOutput;
+        public final String logOutput;
+
+        BootResult(
+                boolean payloadStarted,
                 int deathReason,
                 long apiCallNanoTime,
                 long endToEndNanoTime,
                 OptionalLong vcpuStartedNanoTime,
                 OptionalLong kernelStartedNanoTime,
                 OptionalLong initStartedNanoTime,
-                OptionalLong payloadStartedNanoTime) {
+                OptionalLong payloadStartedNanoTime,
+                String consoleOutput,
+                String logOutput) {
             this.apiCallNanoTime = apiCallNanoTime;
             this.payloadStarted = payloadStarted;
             this.deathReason = deathReason;
@@ -264,6 +286,8 @@
             this.kernelStartedNanoTime = kernelStartedNanoTime;
             this.initStartedNanoTime = initStartedNanoTime;
             this.payloadStartedNanoTime = payloadStartedNanoTime;
+            this.consoleOutput = consoleOutput;
+            this.logOutput = logOutput;
         }
 
         private long getVcpuStartedNanoTime() {
@@ -332,7 +356,9 @@
                 listener.getVcpuStartedNanoTime(),
                 listener.getKernelStartedNanoTime(),
                 listener.getInitStartedNanoTime(),
-                listener.getPayloadStartedNanoTime());
+                listener.getPayloadStartedNanoTime(),
+                listener.getConsoleOutput(),
+                listener.getLogOutput());
     }
 
     /** Execute a command. Returns stdout. */
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 5ce8f60..19ea117 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -334,7 +334,7 @@
         }
 
         // Add partitions to the second disk
-        final String initrdPath = TEST_ROOT + "etc/microdroid_initrd_full_debuggable.img";
+        final String initrdPath = TEST_ROOT + "etc/microdroid_initrd_debuggable.img";
         config.put("initrd", initrdPath);
         // Add instance image as a partition in disks[1]
         disks.put(new JSONObject()
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 4dc9489..edb4759 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -23,6 +23,8 @@
     jni_libs: [
         "MicrodroidTestNativeLib",
         "MicrodroidIdleNativeLib",
+        "MicrodroidEmptyNativeLib",
+        "MicrodroidPrivateLinkingNativeLib",
     ],
     jni_uses_platform_apis: true,
     use_embedded_native_libs: true,
@@ -62,3 +64,21 @@
     header_libs: ["vm_payload_headers"],
     stl: "libc++_static",
 }
+
+// An empty payload missing AVmPayload_main
+cc_library_shared {
+    name: "MicrodroidEmptyNativeLib",
+    srcs: ["src/native/emptybinary.cpp"],
+    stl: "none",
+}
+
+// A payload which tries to link against libselinux, one of private libraries
+cc_library_shared {
+    name: "MicrodroidPrivateLinkingNativeLib",
+    srcs: ["src/native/idlebinary.cpp"],
+    header_libs: ["vm_payload_headers"],
+    // HACK: linking against "libselinux" will embed libselinux.so into the apk
+    // link against a stub to prevent libselinux.so from being embedded
+    shared_libs: ["libselinux#latest"],
+    stl: "libc++_static",
+}
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 57703f3..8b0d6d2 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -18,7 +18,6 @@
 import static android.system.virtualmachine.VirtualMachine.STATUS_DELETED;
 import static android.system.virtualmachine.VirtualMachine.STATUS_RUNNING;
 import static android.system.virtualmachine.VirtualMachine.STATUS_STOPPED;
-import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_APP_ONLY;
 import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_FULL;
 import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_NONE;
 
@@ -520,15 +519,12 @@
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-7"})
     public void changingNonDebuggableVmDebuggableInvalidatesVmIdentity() throws Exception {
         changeDebugLevel(DEBUG_LEVEL_NONE, DEBUG_LEVEL_FULL);
-        changeDebugLevel(DEBUG_LEVEL_NONE, DEBUG_LEVEL_APP_ONLY);
     }
 
     @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-7"})
-    @Ignore("b/260067026")
-    public void changingAppDebuggableVmFullyDebuggableInvalidatesVmIdentity() throws Exception {
-        assume().withMessage("Skip for non-protected VM. b/239158757").that(mProtectedVm).isTrue();
-        changeDebugLevel(DEBUG_LEVEL_APP_ONLY, DEBUG_LEVEL_FULL);
+    public void changingDebuggableVmNonDebuggableInvalidatesVmIdentity() throws Exception {
+        changeDebugLevel(DEBUG_LEVEL_FULL, DEBUG_LEVEL_NONE);
     }
 
     private void changeDebugLevel(int fromLevel, int toLevel) throws Exception {
@@ -838,6 +834,50 @@
                 VirtualMachineCallback.STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR);
     }
 
+    // Checks whether microdroid_launcher started but payload failed. reason must be recorded in the
+    // console output.
+    private void assertThatPayloadFailsDueTo(VirtualMachine vm, String reason) throws Exception {
+        final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
+        final CompletableFuture<Integer> exitCodeFuture = new CompletableFuture<>();
+        VmEventListener listener =
+                new VmEventListener() {
+                    @Override
+                    public void onPayloadStarted(VirtualMachine vm) {
+                        payloadStarted.complete(true);
+                    }
+
+                    @Override
+                    public void onPayloadFinished(VirtualMachine vm, int exitCode) {
+                        exitCodeFuture.complete(exitCode);
+                    }
+                };
+        listener.runToFinish(TAG, vm);
+
+        assertThat(payloadStarted.getNow(false)).isTrue();
+        assertThat(exitCodeFuture.getNow(0)).isNotEqualTo(0);
+        assertThat(listener.getConsoleOutput()).contains(reason);
+    }
+
+    @Test
+    public void bootFailsWhenBinaryIsMissingEntryFunction() throws Exception {
+        VirtualMachineConfig.Builder builder =
+                newVmConfigBuilder().setPayloadBinaryPath("MicrodroidEmptyNativeLib.so");
+        VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
+        VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_missing_entry", normalConfig);
+
+        assertThatPayloadFailsDueTo(vm, "Failed to find entrypoint");
+    }
+
+    @Test
+    public void bootFailsWhenBinaryTriesToLinkAgainstPrivateLibs() throws Exception {
+        VirtualMachineConfig.Builder builder =
+                newVmConfigBuilder().setPayloadBinaryPath("MicrodroidPrivateLinkingNativeLib.so");
+        VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
+        VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_private_linking", normalConfig);
+
+        assertThatPayloadFailsDueTo(vm, "Failed to dlopen");
+    }
+
     @Test
     public void sameInstancesShareTheSameVmObject() throws Exception {
         VirtualMachineConfig config =
diff --git a/tests/testapk/src/native/emptybinary.cpp b/tests/testapk/src/native/emptybinary.cpp
new file mode 100644
index 0000000..15f3f4d
--- /dev/null
+++ b/tests/testapk/src/native/emptybinary.cpp
@@ -0,0 +1 @@
+// a binary without AVmPayload_main
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index e99757e..884561d 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -59,8 +59,6 @@
     enum DebugLevel {
         /** Not debuggable at all */
         NONE,
-        /** Only the logs from app is shown */
-        APP_ONLY,
         /**
          * Fully debuggable. All logs are shown, kernel messages are shown, and adb shell is
          * supported
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 8ac688d..fc85ca5 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -423,7 +423,6 @@
                 let mut vm_metric = self.vm_metric.lock().unwrap();
 
                 // Get CPU Information
-                // TODO: Collect it once right before VM dies using SIGCHLD
                 if let Ok(guest_time) = get_guest_time(pid) {
                     vm_metric.cpu_guest_time = Some(guest_time);
                 } else {
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index f6e8a7b..eb3e9eb 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -405,8 +405,7 @@
 ) -> Result<()> {
     let debug_suffix = match config.debugLevel {
         DebugLevel::NONE => "normal",
-        DebugLevel::APP_ONLY => "app_debuggable",
-        DebugLevel::FULL => "full_debuggable",
+        DebugLevel::FULL => "debuggable",
         _ => return Err(anyhow!("unsupported debug level: {:?}", config.debugLevel)),
     };
     let initrd = format!("/apex/com.android.virt/etc/microdroid_initrd_{}.img", debug_suffix);
diff --git a/vm/src/main.rs b/vm/src/main.rs
index bc18fae..32b165b 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -85,7 +85,7 @@
         #[clap(long)]
         ramdump: Option<PathBuf>,
 
-        /// Debug level of the VM. Supported values: "none" (default), "app_only", and "full".
+        /// Debug level of the VM. Supported values: "none" (default), and "full".
         #[clap(long, default_value = "none", value_parser = parse_debug_level)]
         debug: DebugLevel,
 
@@ -148,7 +148,7 @@
         #[clap(long)]
         ramdump: Option<PathBuf>,
 
-        /// Debug level of the VM. Supported values: "none" (default), "app_only", and "full".
+        /// Debug level of the VM. Supported values: "none" (default), and "full".
         #[clap(long, default_value = "full", value_parser = parse_debug_level)]
         debug: DebugLevel,
 
@@ -233,7 +233,6 @@
 fn parse_debug_level(s: &str) -> Result<DebugLevel, String> {
     match s {
         "none" => Ok(DebugLevel::NONE),
-        "app_only" => Ok(DebugLevel::APP_ONLY),
         "full" => Ok(DebugLevel::FULL),
         _ => Err(format!("Invalid debug level {}", s)),
     }