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 //! Device tree source (dts) for comparing device tree contents
16 //! i.e. sorted dts decompiled by `dtc -s -O dts`.
17 
18 use anyhow::{anyhow, Result};
19 use libfdt::Fdt;
20 use std::io::Write;
21 use std::path::Path;
22 use std::process::{Command, Stdio};
23 
24 /// Device tree source (dts)
25 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
26 pub struct Dts {
27     dts: String,
28 }
29 
30 impl Dts {
31     /// Creates a device tree source from /proc/device-tree style directory
from_fs(path: &Path) -> Result<Self>32     pub fn from_fs(path: &Path) -> Result<Self> {
33         let path = path.to_str().unwrap();
34         let res = Command::new("./dtc_static")
35             .args(["-f", "-s", "-I", "fs", "-O", "dts", path])
36             .output()?;
37         if !res.status.success() {
38             return Err(anyhow!("Failed to run dtc_static, res={res:?}"));
39         }
40         Ok(Self { dts: String::from_utf8(res.stdout)? })
41     }
42 
43     /// Creates a device tree source from dtb
from_dtb(path: &Path) -> Result<Self>44     pub fn from_dtb(path: &Path) -> Result<Self> {
45         let path = path.to_str().unwrap();
46         let res = Command::new("./dtc_static")
47             .args(["-f", "-s", "-I", "dtb", "-O", "dts", path])
48             .output()?;
49         if !res.status.success() {
50             return Err(anyhow!("Failed to run dtc_static, res={res:?}"));
51         }
52         Ok(Self { dts: String::from_utf8(res.stdout)? })
53     }
54 
55     /// Creates a device tree source from Fdt
from_fdt(fdt: &Fdt) -> Result<Self>56     pub fn from_fdt(fdt: &Fdt) -> Result<Self> {
57         let mut dtc = Command::new("./dtc_static")
58             .args(["-f", "-s", "-I", "dtb", "-O", "dts"])
59             .stdin(Stdio::piped())
60             .stdout(Stdio::piped())
61             .spawn()?;
62 
63         {
64             let mut stdin = dtc.stdin.take().unwrap();
65             stdin.write_all(fdt.as_slice())?;
66             // Explicitly drop stdin to avoid indefinite blocking
67         }
68 
69         let res = dtc.wait_with_output()?;
70         if !res.status.success() {
71             return Err(anyhow!("Failed to run dtc_static, res={res:?}"));
72         }
73         Ok(Self { dts: String::from_utf8(res.stdout)? })
74     }
75 }
76