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)),
}