blob: 108ed61bb8bf4750b001ce6764ff39ac76565adb [file] [log] [blame]
// 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");
}
}