Allow host-controlled avf/untrusted prop
Allow addition of properties at /avf/untrusted in the DT overlay
created by virtmgr. While I'm here, refactor reference_dt into
dt_overlay to allow this & remove confusion of reference DT which from
the pt. of view of pVM is expected from ABL.
Include (a hardcoded) instance_id of VM in the DT overlay!
Test: vm_shell start-microdroid --auto-connect
Test: Check /proc/device-tree/avf/untrusted/instance-id has value of hardcoded id in non-protected VM
Test: atest virtualizationmanager_device_test
Bug: 291213394
Revert^2: This reverts commit 655d87fc5b0a0d33baecf40165da2ec1e8f37348.
Reason for revert: Revert^1 was caused by a bug in FsFdt, fixed in
parent commit of the current one.
Change-Id: Ia79fd8dce37f5a13b95c53edef89fe99e225c439
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 771863b..6a8f1a1 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -19,9 +19,9 @@
use crate::composite::make_composite_image;
use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmContext, VmInstance, VmState};
use crate::debug_config::DebugConfig;
+use crate::dt_overlay::{create_device_tree_overlay, VM_DT_OVERLAY_MAX_SIZE, VM_DT_OVERLAY_PATH};
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
use crate::selinux::{getfilecon, SeContext};
-use crate::reference_dt;
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::{
Certificate::Certificate,
@@ -68,6 +68,7 @@
Status, StatusCode, Strong,
IntoBinderResult,
};
+use cstr::cstr;
use disk::QcowFile;
use glob::glob;
use lazy_static::lazy_static;
@@ -79,6 +80,7 @@
use semver::VersionReq;
use std::collections::HashSet;
use std::convert::TryInto;
+use std::fs;
use std::ffi::CStr;
use std::fs::{canonicalize, read_dir, remove_file, File, OpenOptions};
use std::io::{BufRead, BufReader, Error, ErrorKind, Seek, SeekFrom, Write};
@@ -118,6 +120,8 @@
/// crosvm requires all partitions to be a multiple of 4KiB.
const PARTITION_GRANULARITY_BYTES: u64 = 4096;
+const VM_REFERENCE_DT_ON_HOST_PATH: &str = "/proc/device-tree/avf/reference";
+
lazy_static! {
pub static ref GLOBAL_SERVICE: Strong<dyn IVirtualizationServiceInternal> =
wait_for_interface(BINDER_SERVICE_IDENTIFIER)
@@ -376,12 +380,36 @@
check_gdb_allowed(config)?;
}
- let reference_dt = reference_dt::parse_reference_dt(&temporary_directory)
- .context("Failed to create VM reference DT")
- .or_service_specific_exception(-1)?;
- if reference_dt.is_none() {
- warn!("VM reference DT doesn't exist");
- }
+ // Currently, VirtMgr adds the host copy of reference DT & an untrusted prop (instance-id)
+ let host_ref_dt = Path::new(VM_REFERENCE_DT_ON_HOST_PATH);
+ let host_ref_dt = if host_ref_dt.exists()
+ && read_dir(host_ref_dt).or_service_specific_exception(-1)?.next().is_some()
+ {
+ Some(host_ref_dt)
+ } else {
+ warn!("VM reference DT doesn't exist in host DT");
+ None
+ };
+
+ let untrusted_props = if cfg!(llpvm_changes) {
+ // TODO(b/291213394): Replace this with a per-VM instance Id.
+ let instance_id = b"sixtyfourbyteslonghardcoded_indeed_sixtyfourbyteslonghardcoded_h";
+ vec![(cstr!("instance-id"), &instance_id[..])]
+ } else {
+ vec![]
+ };
+
+ let device_tree_overlay = if host_ref_dt.is_some() || !untrusted_props.is_empty() {
+ let dt_output = temporary_directory.join(VM_DT_OVERLAY_PATH);
+ let mut data = [0_u8; VM_DT_OVERLAY_MAX_SIZE];
+ let fdt = create_device_tree_overlay(&mut data, host_ref_dt, &untrusted_props)
+ .map_err(|e| anyhow!("Failed to create DT overlay, {e:?}"))
+ .or_service_specific_exception(-1)?;
+ fs::write(&dt_output, fdt.as_slice()).or_service_specific_exception(-1)?;
+ Some(File::open(dt_output).or_service_specific_exception(-1)?)
+ } else {
+ None
+ };
let debug_level = match config {
VirtualMachineConfig::AppConfig(config) => config.debugLevel,
@@ -531,7 +559,7 @@
gdb_port,
vfio_devices,
dtbo,
- reference_dt,
+ device_tree_overlay,
};
let instance = Arc::new(
VmInstance::new(
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 84c60bd..2c23441 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -118,7 +118,7 @@
pub gdb_port: Option<NonZeroU16>,
pub vfio_devices: Vec<VfioDevice>,
pub dtbo: Option<File>,
- pub reference_dt: Option<File>,
+ pub device_tree_overlay: Option<File>,
}
/// A disk image to pass to crosvm for a VM.
@@ -896,10 +896,8 @@
.arg("--socket")
.arg(add_preserved_fd(&mut preserved_fds, &control_server_socket.as_raw_descriptor()));
- if let Some(reference_dt) = &config.reference_dt {
- command
- .arg("--device-tree-overlay")
- .arg(add_preserved_fd(&mut preserved_fds, reference_dt));
+ if let Some(dt_overlay) = &config.device_tree_overlay {
+ command.arg("--device-tree-overlay").arg(add_preserved_fd(&mut preserved_fds, dt_overlay));
}
append_platform_devices(&mut command, &mut preserved_fds, &config)?;
diff --git a/virtualizationmanager/src/dt_overlay.rs b/virtualizationmanager/src/dt_overlay.rs
new file mode 100644
index 0000000..83f7734
--- /dev/null
+++ b/virtualizationmanager/src/dt_overlay.rs
@@ -0,0 +1,117 @@
+// Copyright 2024, 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.
+
+//! This module support creating AFV related overlays, that can then be appended to DT by VM.
+
+use anyhow::{anyhow, Result};
+use cstr::cstr;
+use fsfdt::FsFdt;
+use libfdt::Fdt;
+use std::ffi::CStr;
+use std::path::Path;
+
+pub(crate) const AVF_NODE_NAME: &CStr = cstr!("avf");
+pub(crate) const UNTRUSTED_NODE_NAME: &CStr = cstr!("untrusted");
+pub(crate) const VM_DT_OVERLAY_PATH: &str = "vm_dt_overlay.dtbo";
+pub(crate) const VM_DT_OVERLAY_MAX_SIZE: usize = 2000;
+
+/// Create a Device tree overlay containing the provided proc style device tree & properties!
+/// # Arguments
+/// * `dt_path` - (Optional) Path to (proc style) device tree to be included in the overlay.
+/// * `untrusted_props` - Include a property in /avf/untrusted node. This node is used to specify
+/// host provided properties such as `instance-id`.
+///
+/// Example: with `create_device_tree_overlay(_, _, [("instance-id", _),])`
+/// ```
+/// {
+/// fragment@0 {
+/// target-path = "/";
+/// __overlay__ {
+/// avf {
+/// untrusted { instance-id = [0x01 0x23 .. ] }
+/// }
+/// };
+/// };
+/// };
+/// };
+/// ```
+pub(crate) fn create_device_tree_overlay<'a>(
+ buffer: &'a mut [u8],
+ dt_path: Option<&'a Path>,
+ untrusted_props: &[(&'a CStr, &'a [u8])],
+) -> Result<&'a mut Fdt> {
+ if dt_path.is_none() && untrusted_props.is_empty() {
+ return Err(anyhow!("Expected at least one device tree addition"));
+ }
+
+ let fdt =
+ Fdt::create_empty_tree(buffer).map_err(|e| anyhow!("Failed to create empty Fdt: {e:?}"))?;
+ let mut root = fdt.root_mut().map_err(|e| anyhow!("Failed to get root: {e:?}"))?;
+ let mut node =
+ root.add_subnode(cstr!("fragment@0")).map_err(|e| anyhow!("Failed to fragment: {e:?}"))?;
+ node.setprop(cstr!("target-path"), b"/\0")
+ .map_err(|e| anyhow!("Failed to set target-path: {e:?}"))?;
+ let mut node = node
+ .add_subnode(cstr!("__overlay__"))
+ .map_err(|e| anyhow!("Failed to __overlay__ node: {e:?}"))?;
+
+ if !untrusted_props.is_empty() {
+ let mut node = node
+ .add_subnode(AVF_NODE_NAME)
+ .map_err(|e| anyhow!("Failed to add avf node: {e:?}"))?;
+ let mut node = node
+ .add_subnode(UNTRUSTED_NODE_NAME)
+ .map_err(|e| anyhow!("Failed to add /avf/untrusted node: {e:?}"))?;
+ for (name, value) in untrusted_props {
+ node.setprop(name, value).map_err(|e| anyhow!("Failed to set property: {e:?}"))?;
+ }
+ }
+
+ if let Some(path) = dt_path {
+ fdt.overlay_onto(cstr!("/fragment@0/__overlay__"), path)?;
+ }
+ fdt.pack().map_err(|e| anyhow!("Failed to pack DT overlay, {e:?}"))?;
+
+ Ok(fdt)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn empty_overlays_not_allowed() {
+ let mut buffer = vec![0_u8; VM_DT_OVERLAY_MAX_SIZE];
+ let res = create_device_tree_overlay(&mut buffer, None, &[]);
+ assert!(res.is_err());
+ }
+
+ #[test]
+ fn untrusted_prop_test() {
+ let mut buffer = vec![0_u8; VM_DT_OVERLAY_MAX_SIZE];
+ let prop_name = cstr!("XOXO");
+ let prop_val_input = b"OXOX";
+ let fdt =
+ create_device_tree_overlay(&mut buffer, None, &[(prop_name, prop_val_input)]).unwrap();
+
+ let prop_value_dt = fdt
+ .node(cstr!("/fragment@0/__overlay__/avf/untrusted"))
+ .unwrap()
+ .expect("/avf/untrusted node doesn't exist")
+ .getprop(prop_name)
+ .unwrap()
+ .expect("Prop not found!");
+ assert_eq!(prop_value_dt, prop_val_input, "Unexpected property value");
+ }
+}
diff --git a/virtualizationmanager/src/main.rs b/virtualizationmanager/src/main.rs
index 2e542c3..b2a734a 100644
--- a/virtualizationmanager/src/main.rs
+++ b/virtualizationmanager/src/main.rs
@@ -19,8 +19,8 @@
mod composite;
mod crosvm;
mod debug_config;
+mod dt_overlay;
mod payload;
-mod reference_dt;
mod selinux;
use crate::aidl::{GLOBAL_SERVICE, VirtualizationService};
diff --git a/virtualizationmanager/src/reference_dt.rs b/virtualizationmanager/src/reference_dt.rs
deleted file mode 100644
index 39b8db8..0000000
--- a/virtualizationmanager/src/reference_dt.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2024, 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.
-
-//! Functions for VM reference DT
-
-use anyhow::{anyhow, Result};
-use cstr::cstr;
-use fsfdt::FsFdt;
-use libfdt::Fdt;
-use std::fs;
-use std::fs::File;
-use std::path::Path;
-
-const VM_REFERENCE_DT_ON_HOST_PATH: &str = "/proc/device-tree/avf/reference";
-const VM_REFERENCE_DT_NAME: &str = "vm_reference_dt.dtbo";
-const VM_REFERENCE_DT_MAX_SIZE: usize = 2000;
-
-// Parses to VM reference if exists.
-// TODO(b/318431695): Allow to parse from custom VM reference DT
-pub(crate) fn parse_reference_dt(out_dir: &Path) -> Result<Option<File>> {
- parse_reference_dt_internal(
- Path::new(VM_REFERENCE_DT_ON_HOST_PATH),
- &out_dir.join(VM_REFERENCE_DT_NAME),
- )
-}
-
-fn parse_reference_dt_internal(dir_path: &Path, fdt_path: &Path) -> Result<Option<File>> {
- if !dir_path.exists() || fs::read_dir(dir_path)?.next().is_none() {
- return Ok(None);
- }
-
- let mut data = vec![0_u8; VM_REFERENCE_DT_MAX_SIZE];
-
- let fdt = Fdt::create_empty_tree(&mut data)
- .map_err(|e| anyhow!("Failed to create an empty DT, {e:?}"))?;
- let mut root = fdt.root_mut().map_err(|e| anyhow!("Failed to find the DT root, {e:?}"))?;
- let mut fragment = root
- .add_subnode(cstr!("fragment@0"))
- .map_err(|e| anyhow!("Failed to create the fragment@0, {e:?}"))?;
- fragment
- .setprop(cstr!("target-path"), b"/\0")
- .map_err(|e| anyhow!("Failed to set target-path, {e:?}"))?;
- fragment
- .add_subnode(cstr!("__overlay__"))
- .map_err(|e| anyhow!("Failed to create the __overlay__, {e:?}"))?;
-
- fdt.overlay_onto(cstr!("/fragment@0/__overlay__"), dir_path)?;
-
- fdt.pack().map_err(|e| anyhow!("Failed to pack VM reference DT, {e:?}"))?;
- fs::write(fdt_path, fdt.as_slice())?;
-
- Ok(Some(File::open(fdt_path)?))
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_parse_reference_dt_from_empty_dir() {
- let empty_dir = tempfile::TempDir::new().unwrap();
- let test_dir = tempfile::TempDir::new().unwrap();
-
- let empty_dir_path = empty_dir.path();
- let fdt_path = test_dir.path().join("test.dtb");
-
- let fdt_file = parse_reference_dt_internal(empty_dir_path, &fdt_path).unwrap();
-
- assert!(fdt_file.is_none());
- }
-
- #[test]
- fn test_parse_reference_dt_from_empty_reference() {
- let fdt_file = parse_reference_dt_internal(
- Path::new("/this/path/would/not/exists"),
- Path::new("test.dtb"),
- )
- .unwrap();
-
- assert!(fdt_file.is_none());
- }
-}