blob: 797ee3c6718363664bd333110e43d5fea0398521 [file] [log] [blame]
Jaewan Kim39952072024-01-19 17:04:53 +09001// 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//! Functions for VM reference DT
16
17use anyhow::{anyhow, Result};
18use cstr::cstr;
19use fsfdt::FsFdt;
20use libfdt::Fdt;
21use std::fs;
22use std::fs::File;
23use std::path::Path;
24
25const VM_REFERENCE_DT_ON_HOST_PATH: &str = "/proc/device-tree/avf/reference";
26const VM_REFERENCE_DT_NAME: &str = "vm_reference_dt.dtbo";
27const VM_REFERENCE_DT_MAX_SIZE: usize = 2000;
28
29// Parses to VM reference if exists.
30// TODO(b/318431695): Allow to parse from custom VM reference DT
31pub(crate) fn parse_reference_dt(out_dir: &Path) -> Result<Option<File>> {
32 parse_reference_dt_internal(
33 Path::new(VM_REFERENCE_DT_ON_HOST_PATH),
34 &out_dir.join(VM_REFERENCE_DT_NAME),
35 )
36}
37
38fn parse_reference_dt_internal(dir_path: &Path, fdt_path: &Path) -> Result<Option<File>> {
39 if !dir_path.exists() || fs::read_dir(dir_path)?.next().is_none() {
40 return Ok(None);
41 }
42
43 let mut data = vec![0_u8; VM_REFERENCE_DT_MAX_SIZE];
44
45 let fdt = Fdt::create_empty_tree(&mut data)
46 .map_err(|e| anyhow!("Failed to create an empty DT, {e:?}"))?;
47 let mut root = fdt.root_mut().map_err(|e| anyhow!("Failed to find the DT root, {e:?}"))?;
48 let mut fragment = root
49 .add_subnode(cstr!("fragment@0"))
50 .map_err(|e| anyhow!("Failed to create the fragment@0, {e:?}"))?;
51 fragment
52 .setprop(cstr!("target-path"), b"/\0")
53 .map_err(|e| anyhow!("Failed to set target-path, {e:?}"))?;
54 fragment
55 .add_subnode(cstr!("__overlay__"))
56 .map_err(|e| anyhow!("Failed to create the __overlay__, {e:?}"))?;
57
58 fdt.append(cstr!("/fragment@0/__overlay__"), dir_path)?;
59
60 fdt.pack().map_err(|e| anyhow!("Failed to pack VM reference DT, {e:?}"))?;
61 fs::write(fdt_path, fdt.as_slice())?;
62
63 Ok(Some(File::open(fdt_path)?))
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn test_parse_reference_dt_from_empty_dir() {
72 let empty_dir = tempfile::TempDir::new().unwrap();
73 let test_dir = tempfile::TempDir::new().unwrap();
74
75 let empty_dir_path = empty_dir.path();
76 let fdt_path = test_dir.path().join("test.dtb");
77
78 let fdt_file = parse_reference_dt_internal(empty_dir_path, &fdt_path).unwrap();
79
80 assert!(fdt_file.is_none());
81 }
82
83 #[test]
84 fn test_parse_reference_dt_from_empty_reference() {
85 let fdt_file = parse_reference_dt_internal(
86 Path::new("/this/path/would/not/exists"),
87 Path::new("test.dtb"),
88 )
89 .unwrap();
90
91 assert!(fdt_file.is_none());
92 }
93}