Merge "[dice] Retrieve the DICE data range from FDT in service VM" into main
diff --git a/microdroid/init_debug_policy/Android.bp b/microdroid/init_debug_policy/Android.bp
index ed017e5..2a87ac9 100644
--- a/microdroid/init_debug_policy/Android.bp
+++ b/microdroid/init_debug_policy/Android.bp
@@ -10,7 +10,6 @@
rustlibs: [
"librustutils",
],
- installable: false, // match with microdroid_init_rc.
bootstrap: true,
prefer_rlib: true,
}
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index e9cb0ec..a496d53 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -144,18 +144,9 @@
Owned(format!("MICRODROID_UNKNOWN_RUNTIME_ERROR|{:?}", err))
};
- let death_reason_bytes = death_reason.as_bytes();
- let mut sent_total = 0;
- while sent_total < death_reason_bytes.len() {
+ for chunk in death_reason.as_bytes().chunks(16) {
// TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
- let begin = sent_total;
- let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
- OpenOptions::new()
- .read(false)
- .write(true)
- .open(FAILURE_SERIAL_DEVICE)?
- .write_all(&death_reason_bytes[begin..end])?;
- sent_total = end;
+ OpenOptions::new().read(false).write(true).open(FAILURE_SERIAL_DEVICE)?.write_all(chunk)?;
}
Ok(())
diff --git a/tests/pvmfw/Android.bp b/tests/pvmfw/Android.bp
index 61667f3..474c62e 100644
--- a/tests/pvmfw/Android.bp
+++ b/tests/pvmfw/Android.bp
@@ -9,6 +9,20 @@
}
genrule {
+ name: "test_avf_debug_policy_with_ramdump",
+ defaults: ["test_avf_dts_to_dtb"],
+ 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_dts_to_dtb"],
+ srcs: ["assets/avf_debug_policy_without_ramdump.dts"],
+ out: ["avf_debug_policy_without_ramdump.dtbo"],
+}
+
+genrule {
name: "test_avf_debug_policy_with_adb",
defaults: ["test_avf_dts_to_dtb"],
srcs: ["assets/avf_debug_policy_with_adb.dts"],
@@ -39,6 +53,8 @@
data: [
":MicrodroidTestApp",
":pvmfw_test",
+ ":test_avf_debug_policy_with_ramdump",
+ ":test_avf_debug_policy_without_ramdump",
":test_avf_debug_policy_with_adb",
":test_avf_debug_policy_without_adb",
"assets/bcc.dat",
diff --git a/tests/pvmfw/assets/avf_debug_policy_with_ramdump.dts b/tests/pvmfw/assets/avf_debug_policy_with_ramdump.dts
new file mode 100644
index 0000000..139d28e
--- /dev/null
+++ b/tests/pvmfw/assets/avf_debug_policy_with_ramdump.dts
@@ -0,0 +1,22 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ fragment@avf {
+ target-path = "/";
+
+ __overlay__ {
+ avf {
+ guest {
+ common {
+ ramdump = <1>;
+ };
+ microdroid {
+ adb = <1>; // adb is required to check VM's bootargs.
+ };
+ };
+ };
+ };
+ };
+};
+
diff --git a/tests/pvmfw/assets/avf_debug_policy_without_ramdump.dts b/tests/pvmfw/assets/avf_debug_policy_without_ramdump.dts
new file mode 100644
index 0000000..8e0e44c
--- /dev/null
+++ b/tests/pvmfw/assets/avf_debug_policy_without_ramdump.dts
@@ -0,0 +1,22 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ fragment@avf {
+ target-path = "/";
+
+ __overlay__ {
+ avf {
+ guest {
+ common {
+ ramdump = <0>;
+ };
+ microdroid {
+ adb = <1>; // adb is required to check VM's bootargs.
+ };
+ };
+ };
+ };
+ };
+};
+
diff --git a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
index 410e6e0..7d0faa4 100644
--- a/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
+++ b/tests/pvmfw/java/com/android/pvmfw/test/DebugPolicyHostTests.java
@@ -192,6 +192,43 @@
launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_FULL);
}
+ @Test
+ public void testRamdumpInDebugPolicy_withDebugLevelNone_hasRamdumpArgs() throws Exception {
+ prepareCustomDebugPolicy("avf_debug_policy_with_ramdump.dtbo");
+ mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_NONE);
+
+ 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 testNoRamdumpInDebugPolicy_withDebugLevelNone_noRamdumpArgs() throws Exception {
+ prepareCustomDebugPolicy("avf_debug_policy_without_ramdump.dtbo");
+ mMicrodroidDevice = launchProtectedVmAndWaitForBootCompleted(MICRODROID_DEBUG_NONE);
+
+ 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 testNoRamdumpInDebugPolicy_withDebugLevelFull_hasRamdumpArgs() throws Exception {
+ prepareCustomDebugPolicy("avf_debug_policy_without_ramdump.dtbo");
+ 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_ZERO);
+ }
+
private boolean isDebugPolicyEnabled(@NonNull String dtPropertyPath)
throws DeviceNotAvailableException {
CommandRunner runner = new CommandRunner(mAndroidDevice);
diff --git a/virtualizationmanager/Android.bp b/virtualizationmanager/Android.bp
index c660414..12d8724 100644
--- a/virtualizationmanager/Android.bp
+++ b/virtualizationmanager/Android.bp
@@ -82,6 +82,8 @@
"libtempfile",
],
data: [
+ ":test_avf_debug_policy_with_ramdump",
+ ":test_avf_debug_policy_without_ramdump",
":test_avf_debug_policy_with_adb",
":test_avf_debug_policy_without_adb",
],
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 790cdb5..8456888 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -18,7 +18,7 @@
use crate::atom::{
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::crosvm::{CrosvmConfig, DiskFile, PayloadState, VfioDevice, VmContext, VmInstance, VmState};
use crate::debug_config::DebugConfig;
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
use crate::selinux::{getfilecon, SeContext};
@@ -458,7 +458,7 @@
}
};
- if !config.devices.is_empty() {
+ let vfio_devices = if !config.devices.is_empty() {
let mut set = HashSet::new();
for device in config.devices.iter() {
let path = canonicalize(device)
@@ -469,8 +469,17 @@
.or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
}
}
- GLOBAL_SERVICE.bindDevicesToVfioDriver(&config.devices)?;
- }
+ GLOBAL_SERVICE
+ .bindDevicesToVfioDriver(&config.devices)?
+ .into_iter()
+ .map(|x| VfioDevice {
+ sysfs_path: PathBuf::from(&x.sysfsPath),
+ dtbo_node: x.dtboNode,
+ })
+ .collect::<Vec<_>>()
+ } else {
+ vec![]
+ };
// Actually start the VM.
let crosvm_config = CrosvmConfig {
@@ -495,7 +504,7 @@
platform_version: parse_platform_version_req(&config.platformVersion)?,
detect_hangup: is_app_config,
gdb_port,
- vfio_devices: config.devices.iter().map(PathBuf::from).collect(),
+ vfio_devices,
};
let instance = Arc::new(
VmInstance::new(
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 77dd76f..b053d99 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -115,7 +115,7 @@
pub platform_version: VersionReq,
pub detect_hangup: bool,
pub gdb_port: Option<NonZeroU16>,
- pub vfio_devices: Vec<PathBuf>,
+ pub vfio_devices: Vec<VfioDevice>,
}
/// A disk image to pass to crosvm for a VM.
@@ -125,6 +125,12 @@
pub writable: bool,
}
+#[derive(Clone, Debug)]
+pub struct VfioDevice {
+ pub sysfs_path: PathBuf,
+ pub dtbo_node: String,
+}
+
/// The lifecycle state which the payload in the VM has reported itself to be in.
///
/// Note that the order of enum variants is significant; only forward transitions are allowed by
@@ -144,7 +150,7 @@
/// The VM has not yet tried to start.
NotStarted {
///The configuration needed to start the VM, if it has not yet been started.
- config: CrosvmConfig,
+ config: Box<CrosvmConfig>,
},
/// The VM has been started.
Running {
@@ -171,7 +177,8 @@
pub struct VmMetric {
/// Recorded timestamp when the VM is started.
pub start_timestamp: Option<SystemTime>,
- /// Update most recent guest_time periodically from /proc/[crosvm pid]/stat while VM is running.
+ /// Update most recent guest_time periodically from /proc/[crosvm pid]/stat while VM is
+ /// running.
pub cpu_guest_time: Option<i64>,
/// Update maximum RSS values periodically from /proc/[crosvm pid]/smaps while VM is running.
pub rss: Option<Rss>,
@@ -184,6 +191,7 @@
fn start(&mut self, instance: Arc<VmInstance>) -> Result<(), Error> {
let state = mem::replace(self, VmState::Failed);
if let VmState::NotStarted { config } = state {
+ let config = *config;
let detect_hangup = config.detect_hangup;
let (failure_pipe_read, failure_pipe_write) = create_pipe()?;
let vfio_devices = config.vfio_devices.clone();
@@ -303,7 +311,7 @@
.flatten()
.map_or_else(|| format!("{}", requester_uid), |u| u.name);
let instance = VmInstance {
- vm_state: Mutex::new(VmState::NotStarted { config }),
+ vm_state: Mutex::new(VmState::NotStarted { config: Box::new(config) }),
vm_context,
cid,
crosvm_control_socket_path: temporary_directory.join("crosvm.sock"),
@@ -342,7 +350,7 @@
&self,
child: Arc<SharedChild>,
mut failure_pipe_read: File,
- vfio_devices: Vec<PathBuf>,
+ vfio_devices: Vec<VfioDevice>,
) {
let result = child.wait();
match &result {
@@ -641,10 +649,10 @@
}
fn death_reason(result: &Result<ExitStatus, io::Error>, mut failure_reason: &str) -> DeathReason {
- if let Some(position) = failure_reason.find('|') {
+ if let Some((reason, info)) = failure_reason.split_once('|') {
// Separator indicates extra context information is present after the failure name.
- error!("Failure info: {}", &failure_reason[(position + 1)..]);
- failure_reason = &failure_reason[..position];
+ error!("Failure info: {info}");
+ failure_reason = reason;
}
if let Ok(status) = result {
match failure_reason {
@@ -694,9 +702,9 @@
const SYSFS_PLATFORM_DEVICES_PATH: &str = "/sys/devices/platform/";
const VFIO_PLATFORM_DRIVER_PATH: &str = "/sys/bus/platform/drivers/vfio-platform";
-fn vfio_argument_for_platform_device(path: &Path) -> Result<String, Error> {
+fn vfio_argument_for_platform_device(device: &VfioDevice) -> Result<String, Error> {
// Check platform device exists
- let path = path.canonicalize()?;
+ let path = device.sysfs_path.canonicalize()?;
if !path.starts_with(SYSFS_PLATFORM_DEVICES_PATH) {
bail!("{path:?} is not a platform device");
}
@@ -708,7 +716,7 @@
}
if let Some(p) = path.to_str() {
- Ok(format!("--vfio={p},iommu=viommu"))
+ Ok(format!("--vfio={p},iommu=viommu,dt-symbol={0}", device.dtbo_node))
} else {
bail!("invalid path {path:?}");
}
diff --git a/virtualizationmanager/src/debug_config.rs b/virtualizationmanager/src/debug_config.rs
index 9b13475..5d22f59 100644
--- a/virtualizationmanager/src/debug_config.rs
+++ b/virtualizationmanager/src/debug_config.rs
@@ -239,6 +239,38 @@
}
#[test]
+ fn test_read_avf_debug_policy_with_ramdump() -> Result<()> {
+ let debug_config = DebugConfig::from_custom_debug_overlay_policy(
+ DebugLevel::FULL,
+ "avf_debug_policy_with_ramdump.dtbo".as_ref(),
+ )
+ .unwrap();
+
+ assert_eq!(DebugLevel::FULL, debug_config.debug_level);
+ assert!(!debug_config.debug_policy_log);
+ assert!(debug_config.debug_policy_ramdump);
+ assert!(debug_config.debug_policy_adb);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_read_avf_debug_policy_without_ramdump() -> Result<()> {
+ let debug_config = DebugConfig::from_custom_debug_overlay_policy(
+ DebugLevel::FULL,
+ "avf_debug_policy_without_ramdump.dtbo".as_ref(),
+ )
+ .unwrap();
+
+ assert_eq!(DebugLevel::FULL, debug_config.debug_level);
+ assert!(!debug_config.debug_policy_log);
+ assert!(!debug_config.debug_policy_ramdump);
+ assert!(debug_config.debug_policy_adb);
+
+ Ok(())
+ }
+
+ #[test]
fn test_read_avf_debug_policy_with_adb() -> Result<()> {
let debug_config = DebugConfig::from_custom_debug_overlay_policy(
DebugLevel::FULL,
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index 62d66c0..f3a7617 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -23,6 +23,10 @@
import android.system.virtualizationservice_internal.IGlobalVmContext;
interface IVirtualizationServiceInternal {
+ parcelable BoundDevice {
+ String sysfsPath;
+ String dtboNode;
+ }
/**
* Removes the memlock rlimit of the calling process.
*
@@ -68,6 +72,7 @@
* Bind given devices to vfio driver.
*
* @param devices paths of sysfs nodes of devices to assign.
+ * @return a list of pairs (sysfs path, DTBO node label) for devices.
*/
- void bindDevicesToVfioDriver(in String[] devices);
+ BoundDevice[] bindDevicesToVfioDriver(in String[] devices);
}
diff --git a/virtualizationservice/assignable_devices.xsd b/virtualizationservice/assignable_devices.xsd
index 842542e..8f43019 100644
--- a/virtualizationservice/assignable_devices.xsd
+++ b/virtualizationservice/assignable_devices.xsd
@@ -25,6 +25,7 @@
</xs:element>
<xs:complexType name="device">
<xs:attribute name="kind" type="xs:string"/>
+ <xs:attribute name="dtbo_node" type="xs:string"/>
<xs:attribute name="sysfs_path" type="xs:string"/>
</xs:complexType>
</xs:schema>
diff --git a/virtualizationservice/schema/current.txt b/virtualizationservice/schema/current.txt
index ed0763a..ef99294 100644
--- a/virtualizationservice/schema/current.txt
+++ b/virtualizationservice/schema/current.txt
@@ -3,8 +3,10 @@
public class Device {
ctor public Device();
+ method public String getDtbo_node();
method public String getKind();
method public String getSysfs_path();
+ method public void setDtbo_node(String);
method public void setKind(String);
method public void setSysfs_path(String);
}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 645a82b..ed5c513 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -28,6 +28,7 @@
AtomVmCreationRequested::AtomVmCreationRequested,
AtomVmExited::AtomVmExited,
IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
+ IVirtualizationServiceInternal::BoundDevice::BoundDevice,
IVirtualizationServiceInternal::IVirtualizationServiceInternal,
IVfioHandler::{BpVfioHandler, IVfioHandler},
};
@@ -179,44 +180,14 @@
fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
check_use_custom_virtual_machine()?;
- let mut ret = Vec::new();
- let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
- if !xml_path.exists() {
- return Ok(ret);
- }
-
- let xml = fs::read(xml_path)
- .context("Failed to read assignable_devices.xml")
- .with_log()
- .or_service_specific_exception(-1)?;
-
- let xml = String::from_utf8(xml)
- .context("assignable_devices.xml is not a valid UTF-8 file")
- .with_log()
- .or_service_specific_exception(-1)?;
-
- let devices: Devices = serde_xml_rs::from_str(&xml)
- .context("can't parse assignable_devices.xml")
- .with_log()
- .or_service_specific_exception(-1)?;
-
- let mut device_set = HashSet::new();
-
- for device in devices.device.into_iter() {
- if device_set.contains(&device.sysfs_path) {
- warn!("duplicated assignable device {device:?}; ignoring...")
- } else if Path::new(&device.sysfs_path).exists() {
- device_set.insert(device.sysfs_path.clone());
- ret.push(AssignableDevice { kind: device.kind, node: device.sysfs_path });
- } else {
- warn!("assignable device {device:?} doesn't exist; ignoring...");
- }
- }
-
- Ok(ret)
+ Ok(get_assignable_devices()?
+ .device
+ .into_iter()
+ .map(|x| AssignableDevice { node: x.sysfs_path, kind: x.kind })
+ .collect::<Vec<_>>())
}
- fn bindDevicesToVfioDriver(&self, devices: &[String]) -> binder::Result<()> {
+ fn bindDevicesToVfioDriver(&self, devices: &[String]) -> binder::Result<Vec<BoundDevice>> {
check_use_custom_virtual_machine()?;
let vfio_service: Strong<dyn IVfioHandler> =
@@ -233,7 +204,17 @@
vfio_service.writeVmDtbo(&ParcelFileDescriptor::new(dtbo))?;
}
- Ok(())
+ Ok(get_assignable_devices()?
+ .device
+ .into_iter()
+ .filter_map(|x| {
+ if devices.contains(&x.sysfs_path) {
+ Some(BoundDevice { sysfsPath: x.sysfs_path, dtboNode: x.dtbo_node })
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>())
}
}
@@ -241,14 +222,54 @@
#[derive(Debug, Deserialize)]
struct Device {
kind: String,
+ dtbo_node: String,
sysfs_path: String,
}
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Default, Deserialize)]
struct Devices {
device: Vec<Device>,
}
+fn get_assignable_devices() -> binder::Result<Devices> {
+ let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
+ if !xml_path.exists() {
+ return Ok(Devices { ..Default::default() });
+ }
+
+ let xml = fs::read(xml_path)
+ .context("Failed to read assignable_devices.xml")
+ .with_log()
+ .or_service_specific_exception(-1)?;
+
+ let xml = String::from_utf8(xml)
+ .context("assignable_devices.xml is not a valid UTF-8 file")
+ .with_log()
+ .or_service_specific_exception(-1)?;
+
+ let mut devices: Devices = serde_xml_rs::from_str(&xml)
+ .context("can't parse assignable_devices.xml")
+ .with_log()
+ .or_service_specific_exception(-1)?;
+
+ let mut device_set = HashSet::new();
+ devices.device.retain(move |device| {
+ if device_set.contains(&device.sysfs_path) {
+ warn!("duplicated assignable device {device:?}; ignoring...");
+ return false;
+ }
+
+ if !Path::new(&device.sysfs_path).exists() {
+ warn!("assignable device {device:?} doesn't exist; ignoring...");
+ return false;
+ }
+
+ device_set.insert(device.sysfs_path.clone());
+ true
+ });
+ Ok(devices)
+}
+
#[derive(Debug, Default)]
struct GlobalVmInstance {
/// The unique CID assigned to the VM for vsock communication.