Handle ramdump in microdroid_manager
Previously pvmfw read AVF debug policy and remove `crashkernel=`
accordingly. However, it unexpectidly disables ramdump for a device
without AVF debug policy even when `--debug=full` is specified.
Changed to read ramdump debug policy in microdroid_manager instead of
pvmfw. microdroid_manager can both debug level and AVF device policy
read, so it might be the better place. Also, pvmfw is now less VM OS aware,
and also need to trust less input from host OS.
Bug: 272752814
Test: atest on devices without AVF debug policy
Change-Id: I3cb4e7425834d5c0deb604e57d28c55e13039f09
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index ffa2e45..8732be1 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -55,8 +55,8 @@
use std::convert::TryInto;
use std::env;
use std::ffi::CString;
-use std::fs::{self, create_dir, OpenOptions};
-use std::io::Write;
+use std::fs::{self, create_dir, OpenOptions, File};
+use std::io::{Read, Write};
use std::os::unix::process::CommandExt;
use std::os::unix::process::ExitStatusExt;
use std::path::Path;
@@ -73,6 +73,7 @@
const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
+const AVF_DEBUG_POLICY_RAMDUMP: &str = "/sys/firmware/devicetree/base/avf/guest/common/ramdump";
const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
"/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
@@ -314,6 +315,21 @@
}
}
+/// 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) -> Result<Option<bool>> {
+ let mut file = match File::open(path) {
+ Ok(dp) => dp,
+ Err(e) => {
+ info!("{e:?}. Assumes <0>");
+ return Ok(Some(false));
+ }
+ };
+ let mut log: [u8; 4] = Default::default();
+ file.read_exact(&mut log).context("Malformed data in {path}")?;
+ // DT spec uses big endian although Android is always little endian.
+ Ok(Some(u32::from_be_bytes(log) == 1))
+}
+
fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
let metadata = load_metadata().context("Failed to load payload metadata")?;
let dice = DiceDriver::new(Path::new("/dev/open-dice0")).context("Failed to load DICE")?;
@@ -781,16 +797,27 @@
}
}
-/// Loads the crashkernel into memory using kexec if the VM is loaded with `crashkernel=' parameter
-/// in the cmdline.
+/// Loads the crashkernel into memory using kexec if debuggable or debug policy says so.
+/// The VM should be loaded with `crashkernel=' parameter in the cmdline to allocate memory
+/// for crashkernel.
fn load_crashkernel_if_supported() -> Result<()> {
let supported = std::fs::read_to_string("/proc/cmdline")?.contains(" crashkernel=");
info!("ramdump supported: {}", supported);
- if supported {
+
+ if !supported {
+ return Ok(());
+ }
+
+ let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
+ let ramdump = get_debug_policy_bool(AVF_DEBUG_POLICY_RAMDUMP)?.unwrap_or_default();
+ let requested = debuggable | ramdump;
+
+ if requested {
let status = Command::new("/system/bin/kexec_load").status()?;
if !status.success() {
return Err(anyhow!("Failed to load crashkernel: {:?}", status));
}
+ info!("ramdump is loaded: debuggable={debuggable}, ramdump={ramdump}");
}
Ok(())
}
diff --git a/pvmfw/src/debug_policy.rs b/pvmfw/src/debug_policy.rs
index 15efa1c..f4b99a6 100644
--- a/pvmfw/src/debug_policy.rs
+++ b/pvmfw/src/debug_policy.rs
@@ -15,7 +15,7 @@
//! Support for the debug policy overlay in pvmfw
use crate::cstr;
-use alloc::{vec, vec::Vec};
+use alloc::vec::Vec;
use core::ffi::CStr;
use core::fmt;
use libfdt::FdtError;
@@ -64,65 +64,6 @@
fdt.pack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to re-pack", e))
}
-/// Disables ramdump by removing crashkernel from bootargs in /chosen.
-fn disable_ramdump(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(()),
- };
-
- // TODO: Improve add 'crashkernel=17MB' only when it's unnecessary.
- // Currently 'crashkernel=17MB' in virtualizationservice and passed by
- // chosen node, because it's not exactly a debug policy but a
- // configuration. However, it's actually microdroid specific
- // so we need a way to generalize it.
- let mut args = vec![];
- for arg in bootargs.to_bytes().split(|byte| byte.is_ascii_whitespace()) {
- if arg.is_empty() || arg.starts_with(b"crashkernel=") {
- continue;
- }
- args.push(arg);
- }
- let mut new_bootargs = args.as_slice().join(&b" "[..]);
- new_bootargs.push(b'\0');
-
- // 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 remove crashkernel. FDT might be corrupted", e)
- })
-}
-
-/// Returns true only if fdt has ramdump prop in the /avf/guest/common node with value <1>
-fn is_ramdump_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!("ramdump"))
- .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to find ramdump prop", e))?
- {
- Some(1) => Ok(true),
- _ => Ok(false),
- }
-}
-
/// 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> {
@@ -191,13 +132,6 @@
apply_debug_policy(fdt, dp)?;
}
- // Handles ramdump in the debug policy
- if is_ramdump_enabled(fdt)? {
- info!("ramdump is enabled by debug policy");
- } else {
- disable_ramdump(fdt)?;
- }
-
// Handles console output in the debug policy
if is_console_output_enabled(fdt)? {
enable_console_output(fdt)?;
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 6330b20..4b5cbda 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -9,20 +9,6 @@
}
genrule {
- name: "test_avf_debug_policy_with_ramdump",
- defaults: ["test_avf_debug_policy_overlay"],
- srcs: ["assets/avf_debug_policy_with_ramdump.dts"],
- out: ["avf_debug_policy_with_ramdump.dtbo"],
-}
-
-genrule {
- name: "test_avf_debug_policy_without_ramdump",
- defaults: ["test_avf_debug_policy_overlay"],
- srcs: ["assets/avf_debug_policy_without_ramdump.dts"],
- out: ["avf_debug_policy_without_ramdump.dtbo"],
-}
-
-genrule {
name: "test_avf_debug_policy_with_log.dtbo",
defaults: ["test_avf_debug_policy_overlay"],
srcs: ["assets/avf_debug_policy_with_log.dts"],
@@ -74,8 +60,6 @@
":test.com.android.virt.pem",
":test2.com.android.virt.pem",
":pvmfw_test",
- ":test_avf_debug_policy_with_ramdump",
- ":test_avf_debug_policy_without_ramdump",
":test_avf_debug_policy_with_log.dtbo",
":test_avf_debug_policy_without_log.dtbo",
":test_avf_debug_policy_with_adb",
diff --git a/tests/hostside/assets/avf_debug_policy_with_ramdump.dts b/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
deleted file mode 100644
index 26db7be..0000000
--- a/tests/hostside/assets/avf_debug_policy_with_ramdump.dts
+++ /dev/null
@@ -1,21 +0,0 @@
-/dts-v1/;
-/plugin/;
-
-/ {
- fragment@avf {
- target-path = "/";
-
- __overlay__ {
- avf {
- guest {
- common {
- ramdump = <1>;
- };
- microdroid {
- adb = <1>; // adb is required to check VM's bootargs.
- };
- };
- };
- };
- };
-};
\ No newline at end of file
diff --git a/tests/hostside/assets/avf_debug_policy_without_ramdump.dts b/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
deleted file mode 100644
index 194e314..0000000
--- a/tests/hostside/assets/avf_debug_policy_without_ramdump.dts
+++ /dev/null
@@ -1,21 +0,0 @@
-/dts-v1/;
-/plugin/;
-
-/ {
- fragment@avf {
- target-path = "/";
-
- __overlay__ {
- avf {
- guest {
- common {
- ramdump = <0>;
- };
- microdroid {
- adb = <1>; // adb is required to check VM's bootargs.
- };
- };
- };
- };
- };
-};
\ No newline at end of file
diff --git a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
index 0d64442..18aa273 100644
--- a/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/PvmfwDebugPolicyHostTests.java
@@ -151,33 +151,6 @@
}
@Test
- public void testRamdump() throws Exception {
- Pvmfw pvmfw = createPvmfw("avf_debug_policy_with_ramdump.dtbo");
- pvmfw.serialize(mCustomPvmfwBinFileOnHost);
- mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
-
- assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH)).contains("crashkernel=");
- assertThat(readMicrodroidFileAsString(MICRODROID_DT_BOOTARGS_PATH))
- .contains("crashkernel=");
- assertThat(readMicrodroidFileAsHexString(MICRODROID_DT_RAMDUMP_PATH))
- .isEqualTo(HEX_STRING_ONE);
- }
-
- @Test
- public void testNoRamdump() throws Exception {
- Pvmfw pvmfw = createPvmfw("avf_debug_policy_without_ramdump.dtbo");
- pvmfw.serialize(mCustomPvmfwBinFileOnHost);
- mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
-
- assertThat(readMicrodroidFileAsString(MICRODROID_CMDLINE_PATH))
- .doesNotContain("crashkernel=");
- assertThat(readMicrodroidFileAsString(MICRODROID_DT_BOOTARGS_PATH))
- .doesNotContain("crashkernel=");
- assertThat(readMicrodroidFileAsHexString(MICRODROID_DT_RAMDUMP_PATH))
- .isEqualTo(HEX_STRING_ZERO);
- }
-
- @Test
public void testLog_consoleOutput() throws Exception {
Pvmfw pvmfw = createPvmfw("avf_debug_policy_with_log.dtbo");
pvmfw.serialize(mCustomPvmfwBinFileOnHost);
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 19f5f01..745d4f6 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -723,8 +723,7 @@
command.arg("--unmap-guest-memory-on-fork");
if config.ramdump.is_some() {
- // Protected VM needs to reserve memory for ramdump here. pvmfw will drop This
- // if ramdump should be disabled (via debug policy). Note that we reserve more
+ // Protected VM needs to reserve memory for ramdump here. Note that we reserve more
// memory for the restricted dma pool.
let ramdump_reserve = RAMDUMP_RESERVED_MIB + swiotlb_size_mib;
command.arg("--params").arg(format!("crashkernel={ramdump_reserve}M"));