Merge "pvmfw: Call CRYPTO_library_init before using BSSL"
diff --git a/microdroid/README.md b/microdroid/README.md
index f70965a..71be7d0 100644
--- a/microdroid/README.md
+++ b/microdroid/README.md
@@ -181,7 +181,8 @@
microdroid can be started with debugging features by debug policies from the
host. Host bootloader may provide debug policies to host OS's device tree for
-VMs.
+VMs. Host bootloader MUST NOT provide debug policies for locked devices for
+security reasons.
For protected VM, such device tree will be available in microdroid. microdroid
can check which debuging features is enabled.
diff --git a/pvmfw/README.md b/pvmfw/README.md
index 41f37b4..2d2b253 100644
--- a/pvmfw/README.md
+++ b/pvmfw/README.md
@@ -245,7 +245,10 @@
Config header can provide a DTBO to be overlaid on top of the baseline device
tree from crosvm.
-The DTBO may contain debug policies as follows.
+The DTBO may contain debug policies. Debug policies MUST NOT be provided for
+locked devices for security reasons.
+
+Here are an example of DTBO.
```
/ {
diff --git a/pvmfw/src/debug_policy.rs b/pvmfw/src/debug_policy.rs
index f4b99a6..bbf7e04 100644
--- a/pvmfw/src/debug_policy.rs
+++ b/pvmfw/src/debug_policy.rs
@@ -14,12 +14,8 @@
//! Support for the debug policy overlay in pvmfw
-use crate::cstr;
-use alloc::vec::Vec;
-use core::ffi::CStr;
use core::fmt;
use libfdt::FdtError;
-use log::info;
#[derive(Debug, Clone)]
pub enum DebugPolicyError {
@@ -64,60 +60,6 @@
fdt.pack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to re-pack", e))
}
-/// Enables console output by adding kernel.printk.devkmsg and kernel.console to bootargs.
-/// This uses hardcoded console name 'hvc0' and it should be match with microdroid's bootconfig.debuggable.
-fn enable_console_output(fdt: &mut libfdt::Fdt) -> Result<(), DebugPolicyError> {
- let chosen = match fdt
- .node(cstr!("/chosen"))
- .map_err(|e| DebugPolicyError::Fdt("Failed to find /chosen", e))?
- {
- Some(node) => node,
- None => return Ok(()),
- };
-
- let bootargs = match chosen
- .getprop_str(cstr!("bootargs"))
- .map_err(|e| DebugPolicyError::Fdt("Failed to find bootargs prop", e))?
- {
- Some(value) if !value.to_bytes().is_empty() => value,
- _ => return Ok(()),
- };
-
- let mut new_bootargs = Vec::from(bootargs.to_bytes());
- new_bootargs.extend_from_slice(b" printk.devkmsg=on console=hvc0\0");
-
- // We'll set larger prop, and need to prepare some room first.
- fdt.unpack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to unpack", e))?;
-
- // We've checked existence of /chosen node at the beginning.
- let mut chosen_mut = fdt.node_mut(cstr!("/chosen")).unwrap().unwrap();
- chosen_mut.setprop(cstr!("bootargs"), new_bootargs.as_slice()).map_err(|e| {
- DebugPolicyError::OverlaidFdt("Failed to enabled console output. FDT might be corrupted", e)
- })?;
-
- fdt.pack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to pack", e))?;
- Ok(())
-}
-
-/// Returns true only if fdt has log prop in the /avf/guest/common node with value <1>
-fn is_console_output_enabled(fdt: &libfdt::Fdt) -> Result<bool, DebugPolicyError> {
- let common = match fdt
- .node(cstr!("/avf/guest/common"))
- .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find /avf/guest/common node", e))?
- {
- Some(node) => node,
- None => return Ok(false),
- };
-
- match common
- .getprop_u32(cstr!("log"))
- .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find log prop", e))?
- {
- Some(1) => Ok(true),
- _ => Ok(false),
- }
-}
-
/// Handles debug policies.
///
/// # Safety
@@ -132,10 +74,5 @@
apply_debug_policy(fdt, dp)?;
}
- // Handles console output in the debug policy
- if is_console_output_enabled(fdt)? {
- enable_console_output(fdt)?;
- info!("console output is enabled by debug policy");
- }
Ok(())
}
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 9851a17..c210ea6 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -45,6 +45,7 @@
import com.android.microdroid.testservice.IBenchmarkService;
import com.android.microdroid.testservice.ITestService;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -54,6 +55,7 @@
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -97,6 +99,19 @@
private Instrumentation mInstrumentation;
+ private boolean mTeardownDebugfs;
+
+ private void setupDebugfs() throws IOException {
+ BufferedReader reader = new BufferedReader(new FileReader("/proc/mounts"));
+
+ mTeardownDebugfs =
+ !reader.lines().filter(line -> line.startsWith("debugfs ")).findAny().isPresent();
+
+ if (mTeardownDebugfs) {
+ executeCommand("mount -t debugfs none /sys/kernel/debug");
+ }
+ }
+
@Before
public void setup() throws IOException {
grantPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION);
@@ -106,6 +121,13 @@
mInstrumentation = getInstrumentation();
}
+ @After
+ public void tearDown() throws IOException {
+ if (mTeardownDebugfs) {
+ executeCommand("umount /sys/kernel/debug");
+ }
+ }
+
private boolean canBootMicrodroidWithMemory(int mem)
throws VirtualMachineException, InterruptedException, IOException {
VirtualMachineConfig normalConfig =
@@ -346,17 +368,15 @@
public final long mGuestRss;
public final long mGuestPss;
- CrosvmStats(Function<String, String> shellExecutor) {
+ CrosvmStats(int vmPid, Function<String, String> shellExecutor) {
try {
- int crosvmPid = ProcessUtil.getCrosvmPid(Os.getpid(), shellExecutor);
-
long hostRss = 0;
long hostPss = 0;
long guestRss = 0;
long guestPss = 0;
boolean hasGuestMaps = false;
for (ProcessUtil.SMapEntry entry :
- ProcessUtil.getProcessSmaps(crosvmPid, shellExecutor)) {
+ ProcessUtil.getProcessSmaps(vmPid, shellExecutor)) {
long rss = entry.metrics.get("Rss");
long pss = entry.metrics.get("Pss");
if (entry.name.contains("crosvm_guest")) {
@@ -383,6 +403,54 @@
}
}
+ private static class KvmVmStats {
+ public final long mProtectedHyp;
+ public final long mProtectedShared;
+ private final Function<String, String> mShellExecutor;
+ private static final String KVM_STATS_FS = "/sys/kernel/debug/kvm";
+
+ public static KvmVmStats createIfSupported(
+ int vmPid, Function<String, String> shellExecutor) {
+
+ if (!new File(KVM_STATS_FS + "/protected_hyp_mem").exists()) {
+ return null;
+ }
+
+ return new KvmVmStats(vmPid, shellExecutor);
+ }
+
+ KvmVmStats(int vmPid, Function<String, String> shellExecutor) {
+ mShellExecutor = shellExecutor;
+
+ try {
+ String dir = getKvmVmStatDir(vmPid);
+
+ mProtectedHyp = getKvmVmStat(dir, "protected_hyp_mem");
+ mProtectedShared = getKvmVmStat(dir, "protected_shared_mem");
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error inside onPayloadReady():" + e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String getKvmVmStatDir(int vmPid) {
+ String output = mShellExecutor.apply("find " + KVM_STATS_FS + " -type d");
+
+ for (String line : output.split("\n")) {
+ if (line.startsWith(KVM_STATS_FS + "/" + Integer.toString(vmPid) + "-")) {
+ return line;
+ }
+ }
+
+ throw new IllegalStateException("KVM stat folder for PID " + vmPid + " not found");
+ }
+
+ private int getKvmVmStat(String dir, String name) throws IOException {
+ return Integer.parseInt(mShellExecutor.apply("cat " + dir + "/" + name).trim());
+ }
+ }
+
@Test
public void testMemoryUsage() throws Exception {
final String vmName = "test_vm_mem_usage";
@@ -394,6 +462,9 @@
.build();
VirtualMachine vm = forceCreateNewVirtualMachine(vmName, config);
MemoryUsageListener listener = new MemoryUsageListener(this::executeCommand);
+
+ setupDebugfs();
+
BenchmarkVmListener.create(listener).runToFinish(TAG, vm);
double mem_overall = 256.0;
@@ -423,6 +494,12 @@
bundle.putDouble(METRIC_NAME_PREFIX + "mem_crosvm_host_pss_MB", mem_crosvm_host_pss);
bundle.putDouble(METRIC_NAME_PREFIX + "mem_crosvm_guest_rss_MB", mem_crosvm_guest_rss);
bundle.putDouble(METRIC_NAME_PREFIX + "mem_crosvm_guest_pss_MB", mem_crosvm_guest_pss);
+ if (listener.mKvm != null) {
+ double mem_protected_shared = (double) listener.mKvm.mProtectedShared / 1048576.0;
+ double mem_protected_hyp = (double) listener.mKvm.mProtectedHyp / 1048576.0;
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_protected_shared_MB", mem_protected_shared);
+ bundle.putDouble(METRIC_NAME_PREFIX + "mem_protected_hyp_MB", mem_protected_hyp);
+ }
mInstrumentation.sendStatus(0, bundle);
}
@@ -441,17 +518,21 @@
public long mSlab;
public CrosvmStats mCrosvm;
+ public KvmVmStats mKvm;
@Override
public void onPayloadReady(VirtualMachine vm, IBenchmarkService service)
throws RemoteException {
+ int vmPid = ProcessUtil.getCrosvmPid(Os.getpid(), mShellExecutor);
+
mMemTotal = service.getMemInfoEntry("MemTotal");
mMemFree = service.getMemInfoEntry("MemFree");
mMemAvailable = service.getMemInfoEntry("MemAvailable");
mBuffers = service.getMemInfoEntry("Buffers");
mCached = service.getMemInfoEntry("Cached");
mSlab = service.getMemInfoEntry("Slab");
- mCrosvm = new CrosvmStats(mShellExecutor);
+ mCrosvm = new CrosvmStats(vmPid, mShellExecutor);
+ mKvm = KvmVmStats.createIfSupported(vmPid, mShellExecutor);
}
}
@@ -511,10 +592,12 @@
@SuppressWarnings("ReturnValueIgnored")
public void onPayloadReady(VirtualMachine vm, IBenchmarkService service)
throws RemoteException {
+ int vmPid = ProcessUtil.getCrosvmPid(Os.getpid(), mShellExecutor);
+
// Allocate 256MB of anonymous memory. This will fill all guest
// memory and cause swapping to start.
service.allocAnonMemory(256);
- mPreCrosvm = new CrosvmStats(mShellExecutor);
+ mPreCrosvm = new CrosvmStats(vmPid, mShellExecutor);
// Send a memory trim hint to cause memory reclaim.
mShellExecutor.apply("am send-trim-memory " + Process.myPid() + " RUNNING_CRITICAL");
// Give time for the memory reclaim to do its work.
@@ -524,7 +607,7 @@
Log.e(TAG, "Interrupted sleep:" + e);
Thread.currentThread().interrupt();
}
- mPostCrosvm = new CrosvmStats(mShellExecutor);
+ mPostCrosvm = new CrosvmStats(vmPid, mShellExecutor);
}
}
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 4b5cbda..248755f 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -9,20 +9,6 @@
}
genrule {
- name: "test_avf_debug_policy_with_log.dtbo",
- defaults: ["test_avf_debug_policy_overlay"],
- srcs: ["assets/avf_debug_policy_with_log.dts"],
- out: ["avf_debug_policy_with_log.dtbo"],
-}
-
-genrule {
- name: "test_avf_debug_policy_without_log.dtbo",
- defaults: ["test_avf_debug_policy_overlay"],
- srcs: ["assets/avf_debug_policy_without_log.dts"],
- out: ["avf_debug_policy_without_log.dtbo"],
-}
-
-genrule {
name: "test_avf_debug_policy_with_adb",
defaults: ["test_avf_debug_policy_overlay"],
srcs: ["assets/avf_debug_policy_with_adb.dts"],
@@ -60,8 +46,6 @@
":test.com.android.virt.pem",
":test2.com.android.virt.pem",
":pvmfw_test",
- ":test_avf_debug_policy_with_log.dtbo",
- ":test_avf_debug_policy_without_log.dtbo",
":test_avf_debug_policy_with_adb",
":test_avf_debug_policy_without_adb",
"assets/bcc.dat",
diff --git a/tests/hostside/assets/avf_debug_policy_with_log.dts b/tests/hostside/assets/avf_debug_policy_with_log.dts
deleted file mode 100644
index 8cf19d6..0000000
--- a/tests/hostside/assets/avf_debug_policy_with_log.dts
+++ /dev/null
@@ -1,18 +0,0 @@
-/dts-v1/;
-/plugin/;
-
-/ {
- fragment@avf {
- target-path = "/";
-
- __overlay__ {
- avf {
- guest {
- common {
- log = <1>;
- };
- };
- };
- };
- };
-};
\ No newline at end of file
diff --git a/tests/hostside/assets/avf_debug_policy_without_log.dts b/tests/hostside/assets/avf_debug_policy_without_log.dts
deleted file mode 100644
index da6400c..0000000
--- a/tests/hostside/assets/avf_debug_policy_without_log.dts
+++ /dev/null
@@ -1,18 +0,0 @@
-/dts-v1/;
-/plugin/;
-
-/ {
- fragment@avf {
- target-path = "/";
-
- __overlay__ {
- avf {
- guest {
- common {
- log = <0>;
- };
- };
- };
- };
- };
-};
\ No newline at end of file
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 2027fcd..dabb31d 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -928,6 +928,21 @@
checkHashAlgorithm(virtApexEtcDir);
}
+ @Test
+ @CddTest
+ public void testNoAvfDebugPolicyInLockedDevice() throws Exception {
+ ITestDevice device = getDevice();
+
+ // Check device's locked state with ro.boot.verifiedbootstate. ro.boot.flash.locked
+ // may not be set if ro.oem_unlock_supported is false.
+ String lockProp = device.getProperty("ro.boot.verifiedbootstate");
+ assumeFalse("Unlocked devices may have AVF debug policy", lockProp.equals("orange"));
+
+ // Test that AVF debug policy doesn't exist.
+ boolean hasDebugPolicy = device.doesFileExist("/sys/firmware/devicetree/base/avf");
+ assertThat(hasDebugPolicy).isFalse();
+ }
+
private String avbInfo(String image_path) throws Exception {
File avbtool = findTestFile("avbtool");
List<String> command =
diff --git a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
index 22131f1..0f6d095 100644
--- a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
@@ -97,6 +97,9 @@
@Before
public void setUp() throws Exception {
mAndroidDevice = (TestDevice) Objects.requireNonNull(getDevice());
+
+ // Check device capabilities
+ assumeDeviceIsCapable(mAndroidDevice);
assumeTrue(
"Skip if protected VMs are not supported",
mAndroidDevice.supportsMicrodroid(/* protectedVm= */ true));
@@ -111,12 +114,6 @@
mBccFileOnHost =
getTestInformation().getDependencyFile(BCC_FILE_NAME, /* targetFirst= */ false);
- // Check device capability
- assumeDeviceIsCapable(mAndroidDevice);
- assumeTrue(
- "Protected VMs are not supported",
- mAndroidDevice.supportsMicrodroid(/*protectedVm=*/ true));
-
// Prepare for loading pvmfw.bin
// File will be setup in individual test,
// and then pushed to device in launchProtectedVmAndWaitForBootCompleted.
@@ -151,56 +148,6 @@
}
@Test
- public void testLog_consoleOutput() throws Exception {
- Pvmfw pvmfw = createPvmfw("avf_debug_policy_with_log.dtbo");
- pvmfw.serialize(mCustomPvmfwBinFileOnHost);
-
- CommandResult result = tryLaunchProtectedNonDebuggableVm();
-
- assertWithMessage("Microdroid's console message should have been enabled")
- .that(hasConsoleOutput(result))
- .isTrue();
- }
-
- @Test
- public void testLog_logcat() throws Exception {
- Pvmfw pvmfw = createPvmfw("avf_debug_policy_with_log.dtbo");
- pvmfw.serialize(mCustomPvmfwBinFileOnHost);
-
- tryLaunchProtectedNonDebuggableVm();
-
- assertWithMessage("Microdroid's logcat should have been enabled")
- .that(hasMicrodroidLogcatOutput())
- .isTrue();
- }
-
- @Test
- public void testNoLog_noConsoleOutput() throws Exception {
- Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_log.dtbo");
- pvmfw.serialize(mCustomPvmfwBinFileOnHost);
-
- CommandResult result = tryLaunchProtectedNonDebuggableVm();
-
- assertWithMessage("Microdroid's console message shouldn't have been disabled")
- .that(hasConsoleOutput(result))
- .isFalse();
- }
-
- @Test
- public void testNoLog_noLogcat() throws Exception {
- Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_log.dtbo");
- pvmfw.serialize(mCustomPvmfwBinFileOnHost);
-
- assertThrows(
- "Microdroid shouldn't be recognized because of missing adb connection",
- DeviceRuntimeException.class,
- () ->
- launchProtectedVmAndWaitForBootCompleted(
- MICRODROID_DEBUG_NONE, BOOT_FAILURE_WAIT_TIME_MS));
- assertThat(hasMicrodroidLogcatOutput()).isFalse();
- }
-
- @Test
public void testAdb_boots() throws Exception {
assumeTrue(
"Skip if host wouldn't install adbd",
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 749d75f..468ee19 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -19,8 +19,7 @@
write_vm_booted_stats, write_vm_creation_stats};
use crate::composite::make_composite_image;
use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmContext, VmInstance, VmState};
-use crate::debug_config::should_prepare_console_output;
-use crate::debug_config::is_ramdump_needed;
+use crate::debug_config::DebugConfig;
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
use crate::selinux::{getfilecon, SeContext};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
@@ -327,21 +326,22 @@
check_gdb_allowed(config)?;
}
- let ramdump = if is_ramdump_needed(config) {
+ let debug_level = match config {
+ VirtualMachineConfig::AppConfig(config) => config.debugLevel,
+ _ => DebugLevel::NONE,
+ };
+ let debug_config = DebugConfig::new(debug_level);
+
+ let ramdump = if debug_config.is_ramdump_needed() {
Some(prepare_ramdump_file(&temporary_directory)?)
} else {
None
};
- let debug_level = match config {
- VirtualMachineConfig::AppConfig(app_config) => app_config.debugLevel,
- _ => DebugLevel::NONE,
- };
-
let state = &mut *self.state.lock().unwrap();
let console_fd =
- clone_or_prepare_logger_fd(config, console_fd, format!("Console({})", cid))?;
- let log_fd = clone_or_prepare_logger_fd(config, log_fd, format!("Log({})", cid))?;
+ clone_or_prepare_logger_fd(&debug_config, console_fd, format!("Console({})", cid))?;
+ let log_fd = clone_or_prepare_logger_fd(&debug_config, log_fd, format!("Log({})", cid))?;
// Counter to generate unique IDs for temporary image files.
let mut next_temporary_image_id = 0;
@@ -352,12 +352,13 @@
let (is_app_config, config) = match config {
VirtualMachineConfig::RawConfig(config) => (false, BorrowedOrOwned::Borrowed(config)),
VirtualMachineConfig::AppConfig(config) => {
- let config = load_app_config(config, &temporary_directory).map_err(|e| {
- *is_protected = config.protectedVm;
- let message = format!("Failed to load app config: {:?}", e);
- error!("{}", message);
- Status::new_service_specific_error_str(-1, Some(message))
- })?;
+ let config =
+ load_app_config(config, &debug_config, &temporary_directory).map_err(|e| {
+ *is_protected = config.protectedVm;
+ let message = format!("Failed to load app config: {:?}", e);
+ error!("{}", message);
+ Status::new_service_specific_error_str(-1, Some(message))
+ })?;
(true, BorrowedOrOwned::Owned(config))
}
};
@@ -438,7 +439,7 @@
disks,
params: config.params.to_owned(),
protected: *is_protected,
- debug_level,
+ debug_config,
memory_mib: config.memoryMib.try_into().ok().and_then(NonZeroU32::new),
cpus,
host_cpu_topology,
@@ -559,6 +560,7 @@
fn load_app_config(
config: &VirtualMachineAppConfig,
+ debug_config: &DebugConfig,
temporary_directory: &Path,
) -> Result<VirtualMachineRawConfig> {
let apk_file = clone_file(config.apk.as_ref().unwrap())?;
@@ -607,6 +609,7 @@
// Include Microdroid payload disk (contains apks, idsigs) in vm config
add_microdroid_payload_images(
config,
+ debug_config,
temporary_directory,
apk_file,
idsig_file,
@@ -1038,7 +1041,7 @@
}
fn clone_or_prepare_logger_fd(
- config: &VirtualMachineConfig,
+ debug_config: &DebugConfig,
fd: Option<&ParcelFileDescriptor>,
tag: String,
) -> Result<Option<File>, Status> {
@@ -1046,10 +1049,7 @@
return Ok(Some(clone_file(fd)?));
}
- let VirtualMachineConfig::AppConfig(app_config) = config else {
- return Ok(None);
- };
- if !should_prepare_console_output(app_config.debugLevel) {
+ if !debug_config.should_prepare_console_output() {
return Ok(None);
};
diff --git a/virtualizationmanager/src/atom.rs b/virtualizationmanager/src/atom.rs
index 567fce9..02d46ec 100644
--- a/virtualizationmanager/src/atom.rs
+++ b/virtualizationmanager/src/atom.rs
@@ -32,7 +32,7 @@
};
use anyhow::{anyhow, Result};
use binder::ParcelFileDescriptor;
-use log::warn;
+use log::{info, warn};
use microdroid_payload_config::VmPayloadConfig;
use statslog_virtualization_rust::vm_creation_requested;
use std::thread;
@@ -149,6 +149,7 @@
apexes,
};
+ info!("Writing VmCreationRequested atom into statsd.");
thread::spawn(move || {
GLOBAL_SERVICE.atomVmCreationRequested(&atom).unwrap_or_else(|e| {
warn!("Failed to write VmCreationRequested atom: {e}");
@@ -172,9 +173,10 @@
elapsedTimeMillis: duration.as_millis() as i64,
};
+ info!("Writing VmBooted atom into statsd.");
thread::spawn(move || {
GLOBAL_SERVICE.atomVmBooted(&atom).unwrap_or_else(|e| {
- warn!("Failed to write VmCreationRequested atom: {e}");
+ warn!("Failed to write VmBooted atom: {e}");
});
});
}
@@ -204,6 +206,7 @@
exitSignal: exit_signal.unwrap_or_default(),
};
+ info!("Writing VmExited atom into statsd.");
thread::spawn(move || {
GLOBAL_SERVICE.atomVmExited(&atom).unwrap_or_else(|e| {
warn!("Failed to write VmExited atom: {e}");
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 7201670..a8cad94 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -16,7 +16,7 @@
use crate::aidl::{remove_temporary_files, Cid, VirtualMachineCallbacks};
use crate::atom::{get_num_cpus, write_vm_exited_stats};
-use crate::debug_config::should_prepare_console_output;
+use crate::debug_config::DebugConfig;
use anyhow::{anyhow, bail, Context, Error, Result};
use command_fds::CommandFdExt;
use lazy_static::lazy_static;
@@ -102,7 +102,7 @@
pub disks: Vec<DiskFile>,
pub params: Option<String>,
pub protected: bool,
- pub debug_level: DebugLevel,
+ pub debug_config: DebugConfig,
pub memory_mib: Option<NonZeroU32>,
pub cpus: Option<NonZeroU32>,
pub host_cpu_topology: bool,
@@ -732,18 +732,15 @@
let ramdump_reserve = RAMDUMP_RESERVED_MIB + swiotlb_size_mib;
command.arg("--params").arg(format!("crashkernel={ramdump_reserve}M"));
}
- } else {
- if config.ramdump.is_some() {
- command.arg("--params").arg(format!("crashkernel={RAMDUMP_RESERVED_MIB}M"));
- }
- if config.debug_level == DebugLevel::NONE
- && should_prepare_console_output(config.debug_level)
- {
- // bootconfig.normal will be used, but we need log.
- // pvmfw will add following commands by itself, but non-protected VM should do so here.
- command.arg("--params").arg("printk.devkmsg=on");
- command.arg("--params").arg("console=hvc0");
- }
+ } else if config.ramdump.is_some() {
+ command.arg("--params").arg(format!("crashkernel={RAMDUMP_RESERVED_MIB}M"));
+ }
+ if config.debug_config.debug_level == DebugLevel::NONE
+ && config.debug_config.should_prepare_console_output()
+ {
+ // bootconfig.normal will be used, but we need log.
+ command.arg("--params").arg("printk.devkmsg=on");
+ command.arg("--params").arg("console=hvc0");
}
if let Some(memory_mib) = config.memory_mib {
diff --git a/virtualizationmanager/src/debug_config.rs b/virtualizationmanager/src/debug_config.rs
index ec3d591..e8863c7 100644
--- a/virtualizationmanager/src/debug_config.rs
+++ b/virtualizationmanager/src/debug_config.rs
@@ -15,10 +15,27 @@
//! Functions for AVF debug policy and debug level
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
- VirtualMachineAppConfig::DebugLevel::DebugLevel, VirtualMachineConfig::VirtualMachineConfig,
+ VirtualMachineAppConfig::DebugLevel::DebugLevel,
};
use std::fs::File;
use std::io::Read;
+use log::info;
+use rustutils::system_properties;
+
+const DEBUG_POLICY_LOG_PATH: &str = "/proc/device-tree/avf/guest/common/log";
+const DEBUG_POLICY_RAMDUMP_PATH: &str = "/proc/device-tree/avf/guest/common/ramdump";
+const DEBUG_POLICY_ADB_PATH: &str = "/proc/device-tree/avf/guest/microdroid/adb";
+
+const SYSPROP_CUSTOM_DEBUG_POLICY_PATH: &str = "hypervisor.virtualizationmanager.debug_policy.path";
+
+/// Debug configurations for both debug level and debug policy
+#[derive(Debug)]
+pub struct DebugConfig {
+ pub debug_level: DebugLevel,
+ debug_policy_log: bool,
+ debug_policy_ramdump: bool,
+ debug_policy_adb: bool,
+}
/// 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> {
@@ -29,28 +46,49 @@
Some(u32::from_be_bytes(log) == 1)
}
-/// Get whether console output should be configred for VM to leave console and adb log.
-/// Caller should create pipe and prepare for receiving VM log with it.
-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()
-}
+impl DebugConfig {
+ pub fn new(debug_level: DebugLevel) -> Self {
+ match system_properties::read(SYSPROP_CUSTOM_DEBUG_POLICY_PATH).unwrap_or_default() {
+ Some(debug_policy_path) if !debug_policy_path.is_empty() => {
+ // TODO: Read debug policy file and override log, adb, ramdump for testing.
+ info!("Debug policy is disabled by sysprop");
+ Self {
+ debug_level,
+ debug_policy_log: false,
+ debug_policy_ramdump: false,
+ debug_policy_adb: false,
+ }
+ }
+ _ => {
+ let debug_config = Self {
+ debug_level,
+ debug_policy_log: get_debug_policy_bool(DEBUG_POLICY_LOG_PATH)
+ .unwrap_or_default(),
+ debug_policy_ramdump: get_debug_policy_bool(DEBUG_POLICY_RAMDUMP_PATH)
+ .unwrap_or_default(),
+ debug_policy_adb: get_debug_policy_bool(DEBUG_POLICY_ADB_PATH)
+ .unwrap_or_default(),
+ };
+ info!("Loaded debug policy from host OS: {:?}", debug_config);
-/// 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()
-}
+ debug_config
+ }
+ }
+ }
-/// 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 debuggable = match config {
- VirtualMachineConfig::RawConfig(_) => false,
- VirtualMachineConfig::AppConfig(config) => config.debugLevel == DebugLevel::FULL,
- };
+ /// Get whether console output should be configred for VM to leave console and adb log.
+ /// Caller should create pipe and prepare for receiving VM log with it.
+ pub fn should_prepare_console_output(&self) -> bool {
+ self.debug_level != DebugLevel::NONE || self.debug_policy_log || self.debug_policy_adb
+ }
- enabled_in_dp || debuggable
+ /// Get whether debug apexes (MICRODROID_REQUIRED_APEXES_DEBUG) are required.
+ pub fn should_include_debug_apexes(&self) -> bool {
+ self.debug_level != DebugLevel::NONE || self.debug_policy_adb
+ }
+
+ /// Decision to support ramdump
+ pub fn is_ramdump_needed(&self) -> bool {
+ self.debug_level != DebugLevel::NONE || self.debug_policy_ramdump
+ }
}
diff --git a/virtualizationmanager/src/payload.rs b/virtualizationmanager/src/payload.rs
index 99aea01..33659d4 100644
--- a/virtualizationmanager/src/payload.rs
+++ b/virtualizationmanager/src/payload.rs
@@ -14,7 +14,7 @@
//! Payload disk image
-use crate::debug_config::should_include_debug_apexes;
+use crate::debug_config::DebugConfig;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
DiskImage::DiskImage,
Partition::Partition,
@@ -257,6 +257,7 @@
/// ..
fn make_payload_disk(
app_config: &VirtualMachineAppConfig,
+ debug_config: &DebugConfig,
apk_file: File,
idsig_file: File,
vm_payload_config: &VmPayloadConfig,
@@ -274,8 +275,7 @@
let apex_list = pm.get_apex_list(vm_payload_config.prefer_staged)?;
// collect APEXes from config
- let mut apex_infos =
- collect_apex_infos(&apex_list, &vm_payload_config.apexes, app_config.debugLevel);
+ let mut apex_infos = collect_apex_infos(&apex_list, &vm_payload_config.apexes, debug_config);
// Pass sorted list of apexes. Sorting key shouldn't use `path` because it will change after
// reboot with prefer_staged. `last_update_seconds` is added to distinguish "samegrade"
@@ -380,10 +380,10 @@
fn collect_apex_infos<'a>(
apex_list: &'a ApexInfoList,
apex_configs: &[ApexConfig],
- debug_level: DebugLevel,
+ debug_config: &DebugConfig,
) -> Vec<&'a ApexInfo> {
let mut additional_apexes: Vec<&str> = MICRODROID_REQUIRED_APEXES.to_vec();
- if should_include_debug_apexes(debug_level) {
+ if debug_config.should_include_debug_apexes() {
additional_apexes.extend(MICRODROID_REQUIRED_APEXES_DEBUG.to_vec());
}
@@ -437,6 +437,7 @@
pub fn add_microdroid_payload_images(
config: &VirtualMachineAppConfig,
+ debug_config: &DebugConfig,
temporary_directory: &Path,
apk_file: File,
idsig_file: File,
@@ -445,6 +446,7 @@
) -> Result<()> {
vm_config.disks.push(make_payload_disk(
config,
+ debug_config,
apk_file,
idsig_file,
vm_payload_config,
@@ -582,7 +584,7 @@
ApexConfig { name: "{CLASSPATH}".to_string() },
];
assert_eq!(
- collect_apex_infos(&apex_info_list, &apex_configs, DebugLevel::FULL),
+ collect_apex_infos(&apex_info_list, &apex_configs, &DebugConfig::new(DebugLevel::FULL)),
vec![
// Pass active/required APEXes
&apex_info_list.list[0],