blob: e176b7be20bbf08e828b8e4ce17a8cb40dba86d1 [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;
Jaewan Kim355fcd82024-01-31 23:36:46 +090019use std::ffi::{CStr, CString};
Jaewan Kim9cf731b2024-01-19 14:45:45 +090020use 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>;
Jaewan Kim355fcd82024-01-31 23:36:46 +090028
Shikha Panwar584f6cb2024-02-13 17:07:30 +000029 /// Overlay an FDT from /proc/device-tree style directory at the given node path
30 fn overlay_onto(&mut self, fdt_node_path: &CStr, fs_path: &Path) -> Result<()>;
Jaewan Kim9cf731b2024-01-19 14:45:45 +090031}
32
33impl<'a> FsFdt<'a> for Fdt {
34 fn from_fs(fs_path: &Path, fdt_buffer: &'a mut [u8]) -> Result<&'a mut Fdt> {
35 let fdt = Fdt::create_empty_tree(fdt_buffer)
36 .map_err(|e| anyhow!("Failed to create FDT, {e:?}"))?;
37
Shikha Panwar584f6cb2024-02-13 17:07:30 +000038 fdt.overlay_onto(&CString::new("").unwrap(), fs_path)?;
Jaewan Kim355fcd82024-01-31 23:36:46 +090039
40 Ok(fdt)
41 }
42
Shikha Panwar584f6cb2024-02-13 17:07:30 +000043 fn overlay_onto(&mut self, fdt_node_path: &CStr, fs_path: &Path) -> Result<()> {
Jaewan Kim9cf731b2024-01-19 14:45:45 +090044 // Recursively traverse fs_path with DFS algorithm.
45 let mut stack = vec![fs_path.to_path_buf()];
46 while let Some(dir_path) = stack.pop() {
47 let relative_path = dir_path
48 .strip_prefix(fs_path)
49 .context("Internal error. Path does not have expected prefix")?
50 .as_os_str();
Jaewan Kim355fcd82024-01-31 23:36:46 +090051 let fdt_path = CString::from_vec_with_nul(
52 [fdt_node_path.to_bytes(), b"/", relative_path.as_bytes(), b"\0"].concat(),
53 )
54 .context("Internal error. Path is not a valid Fdt path")?;
Jaewan Kim9cf731b2024-01-19 14:45:45 +090055
Jaewan Kim355fcd82024-01-31 23:36:46 +090056 let mut node = self
Jaewan Kim9cf731b2024-01-19 14:45:45 +090057 .node_mut(&fdt_path)
58 .map_err(|e| anyhow!("Failed to write FDT, {e:?}"))?
Shikha Panwar584f6cb2024-02-13 17:07:30 +000059 .ok_or_else(|| anyhow!("Failed to find {fdt_path:?} in FDT"))?;
Jaewan Kim9cf731b2024-01-19 14:45:45 +090060
61 let mut subnode_names = vec![];
62 let entries =
63 fs::read_dir(&dir_path).with_context(|| format!("Failed to read {dir_path:?}"))?;
64 for entry in entries {
65 let entry =
66 entry.with_context(|| format!("Failed to get an entry in {dir_path:?}"))?;
67 let entry_type =
68 entry.file_type().with_context(|| "Unsupported entry type, {entry:?}")?;
69 let entry_name = entry.file_name(); // binding to keep name below.
70 if !entry_name.is_ascii() {
71 return Err(anyhow!("Unsupported entry name for FDT, {entry:?}"));
72 }
73 // Safe to unwrap because validated as an ascii string above.
74 let name = CString::new(entry_name.as_bytes()).unwrap();
75 if entry_type.is_dir() {
76 stack.push(entry.path());
77 subnode_names.push(name);
78 } else if entry_type.is_file() {
79 let value = fs::read(&entry.path())?;
80
81 node.setprop(&name, &value)
82 .map_err(|e| anyhow!("Failed to set FDT property, {e:?}"))?;
83 } else {
84 return Err(anyhow!(
85 "Failed to handle {entry:?}. FDT only uses file or directory"
86 ));
87 }
88 }
89 // Note: sort() is necessary to prevent FdtError::Exists from add_subnodes().
90 // FDT library may omit address in node name when comparing their name, so sort to add
91 // node without address first.
92 subnode_names.sort();
Shikha Panwar584f6cb2024-02-13 17:07:30 +000093 let subnode_names: Vec<_> = subnode_names
94 .iter()
95 .filter_map(|name| {
96 // Filter out subnode names which are already present in the target parent node!
97 let name = name.as_c_str();
98 let is_present_res = node.as_node().subnode(name);
99 match is_present_res {
100 Ok(Some(_)) => None,
101 Ok(None) => Some(Ok(name)),
102 Err(e) => Some(Err(e)),
103 }
104 })
105 .collect::<Result<_, _>>()
106 .map_err(|e| anyhow!("Failed to filter subnodes, {e:?}"))?;
107 node.add_subnodes(&subnode_names).map_err(|e| anyhow!("Failed to add node, {e:?}"))?;
Jaewan Kim9cf731b2024-01-19 14:45:45 +0900108 }
109
Jaewan Kim355fcd82024-01-31 23:36:46 +0900110 Ok(())
Jaewan Kim9cf731b2024-01-19 14:45:45 +0900111 }
112}
Jaewan Kimaea90e52024-02-05 08:59:51 +0900113
114#[cfg(test)]
115mod test {
116 use super::*;
Jaewan Kim0bb7cd72024-02-17 00:00:38 +0900117 use dts::Dts;
Jaewan Kimaea90e52024-02-05 08:59:51 +0900118
119 const TEST_FS_FDT_ROOT_PATH: &str = "testdata/fs";
120 const BUF_SIZE_MAX: usize = 1024;
121
Jaewan Kimaea90e52024-02-05 08:59:51 +0900122 #[test]
123 fn test_from_fs() {
124 let fs_path = Path::new(TEST_FS_FDT_ROOT_PATH);
125
126 let mut data = vec![0_u8; BUF_SIZE_MAX];
127 let fdt = Fdt::from_fs(fs_path, &mut data).unwrap();
Jaewan Kimaea90e52024-02-05 08:59:51 +0900128
Jaewan Kim0bb7cd72024-02-17 00:00:38 +0900129 let expected = Dts::from_fs(fs_path).unwrap();
130 let actual = Dts::from_fdt(fdt).unwrap();
Jaewan Kimaea90e52024-02-05 08:59:51 +0900131
132 assert_eq!(&expected, &actual);
Shikha Panwar584f6cb2024-02-13 17:07:30 +0000133 // Again append fdt from TEST_FS_FDT_ROOT_PATH at root & ensure it succeeds when some
134 // subnode are already present.
135 fdt.overlay_onto(&CString::new("/").unwrap(), fs_path).unwrap();
Jaewan Kimaea90e52024-02-05 08:59:51 +0900136 }
137}