| // 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`. |
| /// * `trusted_props` - Include a property in /avf node. This overwrites nodes included with |
| /// `dt_path`. In pVM, pvmfw will reject if it doesn't match the value in pvmfw config. |
| /// |
| /// Example: with `create_device_tree_overlay(_, _, [("instance-id", _),], [("digest", _),])` |
| /// ``` |
| /// { |
| /// fragment@0 { |
| /// target-path = "/"; |
| /// __overlay__ { |
| /// avf { |
| /// digest = [ 0xaa 0xbb .. ] |
| /// 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])], |
| trusted_props: &[(&'a CStr, &'a [u8])], |
| ) -> Result<&'a mut Fdt> { |
| if dt_path.is_none() && untrusted_props.is_empty() && trusted_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 fragment = fdt |
| .root_mut() |
| .add_subnode(cstr!("fragment@0")) |
| .map_err(|e| anyhow!("Failed to add fragment node: {e:?}"))?; |
| fragment |
| .setprop(cstr!("target-path"), b"/\0") |
| .map_err(|e| anyhow!("Failed to set target-path property: {e:?}"))?; |
| let overlay = fragment |
| .add_subnode(cstr!("__overlay__")) |
| .map_err(|e| anyhow!("Failed to add __overlay__ node: {e:?}"))?; |
| let avf = |
| overlay.add_subnode(AVF_NODE_NAME).map_err(|e| anyhow!("Failed to add avf node: {e:?}"))?; |
| |
| if !untrusted_props.is_empty() { |
| let mut untrusted = avf |
| .add_subnode(UNTRUSTED_NODE_NAME) |
| .map_err(|e| anyhow!("Failed to add untrusted node: {e:?}"))?; |
| for (name, value) in untrusted_props { |
| untrusted |
| .setprop(name, value) |
| .map_err(|e| anyhow!("Failed to set untrusted property: {e:?}"))?; |
| } |
| } |
| |
| // Read dt_path from host DT and overlay onto fdt. |
| if let Some(path) = dt_path { |
| fdt.overlay_onto(cstr!("/fragment@0/__overlay__"), path)?; |
| } |
| |
| if !trusted_props.is_empty() { |
| let mut avf = fdt |
| .node_mut(cstr!("/fragment@0/__overlay__/avf")) |
| .map_err(|e| anyhow!("Failed to search avf node: {e:?}"))? |
| .ok_or(anyhow!("Failed to get avf node"))?; |
| for (name, value) in trusted_props { |
| avf.setprop(name, value) |
| .map_err(|e| anyhow!("Failed to set trusted property: {e:?}"))?; |
| } |
| } |
| |
| 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"); |
| } |
| |
| #[test] |
| fn trusted_prop_test() { |
| let mut buffer = vec![0_u8; VM_DT_OVERLAY_MAX_SIZE]; |
| let prop_name = cstr!("XOXOXO"); |
| let prop_val_input = b"OXOXOX"; |
| 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")) |
| .unwrap() |
| .expect("/avf node doesn't exist") |
| .getprop(prop_name) |
| .unwrap() |
| .expect("Prop not found!"); |
| assert_eq!(prop_value_dt, prop_val_input, "Unexpected property value"); |
| } |
| } |