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