1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This module support creating AFV related overlays, that can then be appended to DT by VM.
16 
17 use anyhow::{anyhow, Result};
18 use cstr::cstr;
19 use fsfdt::FsFdt;
20 use libfdt::Fdt;
21 use std::ffi::CStr;
22 use std::path::Path;
23 
24 pub(crate) const AVF_NODE_NAME: &CStr = cstr!("avf");
25 pub(crate) const UNTRUSTED_NODE_NAME: &CStr = cstr!("untrusted");
26 pub(crate) const VM_DT_OVERLAY_PATH: &str = "vm_dt_overlay.dtbo";
27 pub(crate) const VM_DT_OVERLAY_MAX_SIZE: usize = 2000;
28 
29 /// Create a Device tree overlay containing the provided proc style device tree & properties!
30 /// # Arguments
31 /// * `dt_path` - (Optional) Path to (proc style) device tree to be included in the overlay.
32 /// * `untrusted_props` - Include a property in /avf/untrusted node. This node is used to specify
33 ///   host provided properties such as `instance-id`.
34 /// * `trusted_props` - Include a property in /avf node. This overwrites nodes included with
35 ///   `dt_path`. In pVM, pvmfw will reject if it doesn't match the value in pvmfw config.
36 ///
37 /// Example: with `create_device_tree_overlay(_, _, [("instance-id", _),], [("digest", _),])`
38 /// ```
39 ///   {
40 ///     fragment@0 {
41 ///         target-path = "/";
42 ///         __overlay__ {
43 ///             avf {
44 ///                 digest = [ 0xaa 0xbb .. ]
45 ///                 untrusted { instance-id = [ 0x01 0x23 .. ] }
46 ///               }
47 ///             };
48 ///         };
49 ///     };
50 /// };
51 /// ```
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>52 pub(crate) fn create_device_tree_overlay<'a>(
53     buffer: &'a mut [u8],
54     dt_path: Option<&'a Path>,
55     untrusted_props: &[(&'a CStr, &'a [u8])],
56     trusted_props: &[(&'a CStr, &'a [u8])],
57 ) -> Result<&'a mut Fdt> {
58     if dt_path.is_none() && untrusted_props.is_empty() && trusted_props.is_empty() {
59         return Err(anyhow!("Expected at least one device tree addition"));
60     }
61 
62     let fdt =
63         Fdt::create_empty_tree(buffer).map_err(|e| anyhow!("Failed to create empty Fdt: {e:?}"))?;
64     let mut fragment = fdt
65         .root_mut()
66         .add_subnode(cstr!("fragment@0"))
67         .map_err(|e| anyhow!("Failed to add fragment node: {e:?}"))?;
68     fragment
69         .setprop(cstr!("target-path"), b"/\0")
70         .map_err(|e| anyhow!("Failed to set target-path property: {e:?}"))?;
71     let overlay = fragment
72         .add_subnode(cstr!("__overlay__"))
73         .map_err(|e| anyhow!("Failed to add __overlay__ node: {e:?}"))?;
74     let avf =
75         overlay.add_subnode(AVF_NODE_NAME).map_err(|e| anyhow!("Failed to add avf node: {e:?}"))?;
76 
77     if !untrusted_props.is_empty() {
78         let mut untrusted = avf
79             .add_subnode(UNTRUSTED_NODE_NAME)
80             .map_err(|e| anyhow!("Failed to add untrusted node: {e:?}"))?;
81         for (name, value) in untrusted_props {
82             untrusted
83                 .setprop(name, value)
84                 .map_err(|e| anyhow!("Failed to set untrusted property: {e:?}"))?;
85         }
86     }
87 
88     // Read dt_path from host DT and overlay onto fdt.
89     if let Some(path) = dt_path {
90         fdt.overlay_onto(cstr!("/fragment@0/__overlay__"), path)?;
91     }
92 
93     if !trusted_props.is_empty() {
94         let mut avf = fdt
95             .node_mut(cstr!("/fragment@0/__overlay__/avf"))
96             .map_err(|e| anyhow!("Failed to search avf node: {e:?}"))?
97             .ok_or(anyhow!("Failed to get avf node"))?;
98         for (name, value) in trusted_props {
99             avf.setprop(name, value)
100                 .map_err(|e| anyhow!("Failed to set trusted property: {e:?}"))?;
101         }
102     }
103 
104     fdt.pack().map_err(|e| anyhow!("Failed to pack DT overlay, {e:?}"))?;
105 
106     Ok(fdt)
107 }
108 
109 #[cfg(test)]
110 mod tests {
111     use super::*;
112 
113     #[test]
empty_overlays_not_allowed()114     fn empty_overlays_not_allowed() {
115         let mut buffer = vec![0_u8; VM_DT_OVERLAY_MAX_SIZE];
116         let res = create_device_tree_overlay(&mut buffer, None, &[], &[]);
117         assert!(res.is_err());
118     }
119 
120     #[test]
untrusted_prop_test()121     fn untrusted_prop_test() {
122         let mut buffer = vec![0_u8; VM_DT_OVERLAY_MAX_SIZE];
123         let prop_name = cstr!("XOXO");
124         let prop_val_input = b"OXOX";
125         let fdt =
126             create_device_tree_overlay(&mut buffer, None, &[(prop_name, prop_val_input)], &[])
127                 .unwrap();
128 
129         let prop_value_dt = fdt
130             .node(cstr!("/fragment@0/__overlay__/avf/untrusted"))
131             .unwrap()
132             .expect("/avf/untrusted node doesn't exist")
133             .getprop(prop_name)
134             .unwrap()
135             .expect("Prop not found!");
136         assert_eq!(prop_value_dt, prop_val_input, "Unexpected property value");
137     }
138 
139     #[test]
trusted_prop_test()140     fn trusted_prop_test() {
141         let mut buffer = vec![0_u8; VM_DT_OVERLAY_MAX_SIZE];
142         let prop_name = cstr!("XOXOXO");
143         let prop_val_input = b"OXOXOX";
144         let fdt =
145             create_device_tree_overlay(&mut buffer, None, &[], &[(prop_name, prop_val_input)])
146                 .unwrap();
147 
148         let prop_value_dt = fdt
149             .node(cstr!("/fragment@0/__overlay__/avf"))
150             .unwrap()
151             .expect("/avf node doesn't exist")
152             .getprop(prop_name)
153             .unwrap()
154             .expect("Prop not found!");
155         assert_eq!(prop_value_dt, prop_val_input, "Unexpected property value");
156     }
157 }
158