blob: cda2fe1e4cd904bb5223dea18e18b81a2394510f [file] [log] [blame]
Jaewan Kim9cf731b2024-01-19 14:45:45 +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//! Implements converting file system to FDT blob
16
17use anyhow::{anyhow, Context, Result};
18use libfdt::Fdt;
19use std::ffi::CString;
20use std::fs;
21use std::os::unix::ffi::OsStrExt;
22use std::path::Path;
23
24/// Trait for Fdt's file system support
25pub trait FsFdt<'a> {
26 /// Creates a Fdt from /proc/device-tree style directory by wrapping a mutable slice
27 fn from_fs(fs_path: &Path, fdt_buffer: &'a mut [u8]) -> Result<&'a mut Self>;
28}
29
30impl<'a> FsFdt<'a> for Fdt {
31 fn from_fs(fs_path: &Path, fdt_buffer: &'a mut [u8]) -> Result<&'a mut Fdt> {
32 let fdt = Fdt::create_empty_tree(fdt_buffer)
33 .map_err(|e| anyhow!("Failed to create FDT, {e:?}"))?;
34
35 // Recursively traverse fs_path with DFS algorithm.
36 let mut stack = vec![fs_path.to_path_buf()];
37 while let Some(dir_path) = stack.pop() {
38 let relative_path = dir_path
39 .strip_prefix(fs_path)
40 .context("Internal error. Path does not have expected prefix")?
41 .as_os_str();
42 let fdt_path =
43 CString::from_vec_with_nul([b"/", relative_path.as_bytes(), b"\0"].concat())
44 .context("Internal error. Path is not a valid Fdt path")?;
45
46 let mut node = fdt
47 .node_mut(&fdt_path)
48 .map_err(|e| anyhow!("Failed to write FDT, {e:?}"))?
49 .ok_or_else(|| anyhow!("Internal error when writing VM reference DT"))?;
50
51 let mut subnode_names = vec![];
52 let entries =
53 fs::read_dir(&dir_path).with_context(|| format!("Failed to read {dir_path:?}"))?;
54 for entry in entries {
55 let entry =
56 entry.with_context(|| format!("Failed to get an entry in {dir_path:?}"))?;
57 let entry_type =
58 entry.file_type().with_context(|| "Unsupported entry type, {entry:?}")?;
59 let entry_name = entry.file_name(); // binding to keep name below.
60 if !entry_name.is_ascii() {
61 return Err(anyhow!("Unsupported entry name for FDT, {entry:?}"));
62 }
63 // Safe to unwrap because validated as an ascii string above.
64 let name = CString::new(entry_name.as_bytes()).unwrap();
65 if entry_type.is_dir() {
66 stack.push(entry.path());
67 subnode_names.push(name);
68 } else if entry_type.is_file() {
69 let value = fs::read(&entry.path())?;
70
71 node.setprop(&name, &value)
72 .map_err(|e| anyhow!("Failed to set FDT property, {e:?}"))?;
73 } else {
74 return Err(anyhow!(
75 "Failed to handle {entry:?}. FDT only uses file or directory"
76 ));
77 }
78 }
79 // Note: sort() is necessary to prevent FdtError::Exists from add_subnodes().
80 // FDT library may omit address in node name when comparing their name, so sort to add
81 // node without address first.
82 subnode_names.sort();
83 let subnode_names_c_str: Vec<_> = subnode_names.iter().map(|x| x.as_c_str()).collect();
84 node.add_subnodes(&subnode_names_c_str)
85 .map_err(|e| anyhow!("Failed to add node, {e:?}"))?;
86 }
87
88 Ok(fdt)
89 }
90}