Add libdts for comparing DTs
dtdiff script compares DTs of various formats, but it can't be used
because it expects bash extension.
libdts decompiles DTs with statically built dtc with sort option, so can
be used in Android instead of dtdiff.
Bug: 277993056
Test: atest libfsfdt_test
Change-Id: I69187beeb544afeb5206fed22dc824165b0dc410
diff --git a/tests/libs/libdts/src/lib.rs b/tests/libs/libdts/src/lib.rs
new file mode 100644
index 0000000..0ee9b66
--- /dev/null
+++ b/tests/libs/libdts/src/lib.rs
@@ -0,0 +1,75 @@
+// Copyright 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Device tree source (dts) for comparing device tree contents
+//! i.e. sorted dts decompiled by `dtc -s -O dts`.
+
+use anyhow::{anyhow, Result};
+use libfdt::Fdt;
+use std::io::Write;
+use std::path::Path;
+use std::process::{Command, Stdio};
+
+/// Device tree source (dts)
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
+pub struct Dts {
+ dts: String,
+}
+
+impl Dts {
+ /// Creates a device tree source from /proc/device-tree style directory
+ pub fn from_fs(path: &Path) -> Result<Self> {
+ let path = path.to_str().unwrap();
+ let res = Command::new("./dtc_static")
+ .args(["-f", "-s", "-I", "fs", "-O", "dts", path])
+ .output()?;
+ if !res.status.success() {
+ return Err(anyhow!("Failed to run dtc_static, res={res:?}"));
+ }
+ Ok(Self { dts: String::from_utf8(res.stdout)? })
+ }
+
+ /// Creates a device tree source from dtb
+ pub fn from_dtb(path: &Path) -> Result<Self> {
+ let path = path.to_str().unwrap();
+ let res = Command::new("./dtc_static")
+ .args(["-f", "-s", "-I", "dtb", "-O", "dts", path])
+ .output()?;
+ if !res.status.success() {
+ return Err(anyhow!("Failed to run dtc_static, res={res:?}"));
+ }
+ Ok(Self { dts: String::from_utf8(res.stdout)? })
+ }
+
+ /// Creates a device tree source from Fdt
+ pub fn from_fdt(fdt: &Fdt) -> Result<Self> {
+ let mut dtc = Command::new("./dtc_static")
+ .args(["-f", "-s", "-I", "dtb", "-O", "dts"])
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn()?;
+
+ {
+ let mut stdin = dtc.stdin.take().unwrap();
+ stdin.write_all(fdt.as_slice())?;
+ // Explicitly drop stdin to avoid indefinite blocking
+ }
+
+ let res = dtc.wait_with_output()?;
+ if !res.status.success() {
+ return Err(anyhow!("Failed to run dtc_static, res={res:?}"));
+ }
+ Ok(Self { dts: String::from_utf8(res.stdout)? })
+ }
+}