use dtoolkit::fdt::Fdt;
#[cfg(feature = "write")]
use dtoolkit::model::DeviceTree;
use dtoolkit::standard::{InitialMappedArea, NodeStandard, Status};
use dtoolkit::{Node, Property};
#[test]
fn read_child_nodes() {
let dtb = include_bytes!("dtb/test_children.dtb");
let fdt = Fdt::new(dtb).unwrap();
let root = fdt.root();
let mut children = root.children();
let child1 = children.next().unwrap();
assert_eq!(child1.name(), "child1");
assert_eq!(child1.name_without_address(), "child1");
let child3 = children.next().unwrap();
assert_eq!(child3.name(), "child2@42");
assert_eq!(child3.name_without_address(), "child2");
assert!(children.next().is_none());
}
#[test]
fn name_outlives_fdt_and_node() {
let dtb = include_bytes!("dtb/test_children.dtb");
let name = {
let fdt = Fdt::new(dtb).unwrap();
let child1 = fdt.find_node("/child1").unwrap();
child1.name()
};
assert_eq!(name, "child1");
}
#[test]
fn read_prop_values() {
let dtb = include_bytes!("dtb/test_props.dtb");
let fdt = Fdt::new(dtb).unwrap();
let root = fdt.root();
let mut children = root.children();
let node = children.next().unwrap();
assert_eq!(node.name(), "test-props");
let mut props = node.properties();
let prop = props.next().unwrap();
assert_eq!(prop.name(), "u32-prop");
assert_eq!(prop.as_u32().unwrap(), 0x1234_5678);
let prop = props.next().unwrap();
assert_eq!(prop.name(), "u64-prop");
assert_eq!(prop.as_u64().unwrap(), 0x1122_3344_5566_7788);
let prop = props.next().unwrap();
assert_eq!(prop.name(), "str-prop");
assert_eq!(prop.as_str().unwrap(), "hello world");
let prop = props.next().unwrap();
assert_eq!(prop.name(), "str-list-prop");
let mut str_list = prop.as_str_list();
assert_eq!(str_list.next(), Some("first"));
assert_eq!(str_list.next(), Some("second"));
assert_eq!(str_list.next(), Some("third"));
assert_eq!(str_list.next(), None);
assert!(props.next().is_none());
}
#[test]
fn get_property_by_name() {
let dtb = include_bytes!("dtb/test_props.dtb");
let fdt = Fdt::new(dtb).unwrap();
let root = fdt.root();
let node = root.child("test-props").unwrap();
let prop = node.property("u32-prop").unwrap();
assert_eq!(prop.name(), "u32-prop");
assert_eq!(prop.as_u32().unwrap(), 0x1234_5678);
let prop = node.property("str-prop").unwrap();
assert_eq!(prop.name(), "str-prop");
assert_eq!(prop.as_str().unwrap(), "hello world");
assert!(node.property("non-existent-prop").is_none());
}
#[test]
fn standard_properties() {
let dtb = include_bytes!("dtb/test_props.dtb");
let fdt = Fdt::new(dtb).unwrap();
let root = fdt.root();
let test_props_node = root.child("test-props").unwrap();
let standard_props_node = root.child("standard-props").unwrap();
assert_eq!(test_props_node.address_cells().unwrap(), 2);
assert_eq!(test_props_node.size_cells().unwrap(), 1);
assert_eq!(test_props_node.status().unwrap(), Status::Okay);
assert_eq!(test_props_node.model().unwrap(), None);
assert!(!test_props_node.dma_coherent());
assert_eq!(test_props_node.phandle().unwrap(), None);
assert_eq!(test_props_node.virtual_reg().unwrap(), None);
assert!(test_props_node.ranges().unwrap().is_none());
assert!(test_props_node.dma_ranges().unwrap().is_none());
assert!(test_props_node.compatible().is_none());
assert_eq!(standard_props_node.address_cells().unwrap(), 1);
assert_eq!(standard_props_node.size_cells().unwrap(), 1);
assert_eq!(standard_props_node.status().unwrap(), Status::Fail);
assert_eq!(standard_props_node.model().unwrap(), Some("Some Model"));
assert!(standard_props_node.dma_coherent());
assert_eq!(standard_props_node.phandle().unwrap(), Some(0x1234));
assert_eq!(standard_props_node.virtual_reg().unwrap(), Some(0xabcd));
assert_eq!(
standard_props_node
.compatible()
.unwrap()
.collect::<Vec<_>>(),
vec!["abc,def", "some,other"]
);
let reg = standard_props_node
.reg()
.unwrap()
.unwrap()
.collect::<Vec<_>>();
assert_eq!(reg.len(), 2);
assert_eq!(reg[0].address::<u64>().unwrap(), 0x1234_5678_0000_3000);
assert_eq!(reg[0].size::<u64>().unwrap(), 32);
assert_eq!(reg[1].address::<u64>().unwrap(), 0xfe00);
assert_eq!(reg[1].size::<u64>().unwrap(), 256);
let ranges = standard_props_node
.ranges()
.unwrap()
.unwrap()
.collect::<Vec<_>>();
assert_eq!(ranges.len(), 1);
assert_eq!(ranges[0].child_bus_address::<u32>().unwrap(), 0x1111_0000);
assert_eq!(
ranges[0].parent_bus_address::<u64>().unwrap(),
0x2222_0000_3333_0000
);
assert_eq!(ranges[0].length::<u32>().unwrap(), 0x4444_0000);
let dma_ranges = standard_props_node
.dma_ranges()
.unwrap()
.unwrap()
.collect::<Vec<_>>();
assert_eq!(dma_ranges.len(), 1);
assert_eq!(dma_ranges[0].child_bus_address::<u32>().unwrap(), 0xaaaa);
assert_eq!(
dma_ranges[0].parent_bus_address::<u64>().unwrap(),
0xbbbb_0000_cccc
);
assert_eq!(dma_ranges[0].length::<u32>().unwrap(), 0xdddd);
}
#[test]
fn get_child_by_name() {
let dtb = include_bytes!("dtb/test_children.dtb");
let fdt = Fdt::new(dtb).unwrap();
let root = fdt.root();
let child1 = root.child("child1").unwrap();
assert_eq!(child1.name(), "child1");
let child2 = root.child("child2").unwrap();
assert_eq!(child2.name(), "child2@42");
let child2_with_address = root.child("child2@42").unwrap();
assert_eq!(child2_with_address.name(), "child2@42");
assert!(root.child("non-existent-child").is_none());
}
#[test]
fn children_nested() {
let dtb = include_bytes!("dtb/test_children_nested.dtb");
let fdt = Fdt::new(dtb).unwrap();
let root = fdt.root();
for child in root.children() {
println!("{}", child.name());
}
let children_names: Vec<_> = root.children().map(|child| child.name()).collect();
assert_eq!(children_names, vec!["child1", "child3"]);
let child1 = root.child("child1").unwrap();
let child2 = child1.child("child2").unwrap();
let nested_properties: Vec<_> = child2
.properties()
.map(|prop| prop.name().to_owned())
.collect();
assert_eq!(nested_properties, vec!["prop2"]);
}
#[test]
fn find_node_by_path() {
let dtb = include_bytes!("dtb/test_traversal.dtb");
let fdt = Fdt::new(dtb).unwrap();
let root = fdt.find_node("/").unwrap();
assert_eq!(root.name(), "");
let a = fdt.find_node("/a").unwrap();
assert_eq!(a.name(), "a");
let b = fdt.find_node("/a/b").unwrap();
assert_eq!(b.name(), "b");
let c = fdt.find_node("/a/b/c").unwrap();
assert_eq!(c.name(), "c");
let d = fdt.find_node("/d").unwrap();
assert_eq!(d.name(), "d");
assert!(fdt.find_node("/a/c").is_none());
assert!(fdt.find_node("/x").is_none());
assert!(fdt.find_node("").is_none());
}
#[test]
fn memory() {
let dtb = include_bytes!("dtb/test_pretty_print.dtb");
let fdt = Fdt::new(dtb).unwrap();
let memory = fdt.memory().unwrap();
let reg = memory.reg().unwrap().unwrap().collect::<Vec<_>>();
assert_eq!(reg.len(), 1);
assert_eq!(reg[0].address::<u32>().unwrap(), 0x8000_0000);
assert_eq!(reg[0].size::<u32>().unwrap(), 0x2000_0000);
assert!(memory.hotpluggable());
assert_eq!(
memory
.initial_mapped_area()
.unwrap()
.unwrap()
.collect::<Vec<_>>(),
vec![InitialMappedArea {
effective_address: 0x1234,
physical_address: 0x4321,
size: 0x1000,
}]
);
}
#[test]
fn reserved_memory() {
let dtb = include_bytes!("dtb/test_pretty_print.dtb");
let fdt = Fdt::new(dtb).unwrap();
let reserved = fdt.reserved_memory().unwrap().collect::<Vec<_>>();
assert!(reserved[0].reg().unwrap().is_none());
assert_eq!(
reserved[0]
.size()
.unwrap()
.unwrap()
.to_int::<u32>()
.unwrap(),
0x400_0000
);
assert_eq!(
reserved[0]
.alignment()
.unwrap()
.unwrap()
.to_int::<u32>()
.unwrap(),
0x2000
);
assert!(reserved[0].reusable());
assert!(reserved[1].size().unwrap().is_none());
assert!(reserved[1].alignment().unwrap().is_none());
assert!(!reserved[1].reusable());
let reg = reserved[1].reg().unwrap().unwrap().collect::<Vec<_>>();
assert_eq!(reg.len(), 1);
assert_eq!(reg[0].address::<u32>().unwrap(), 0x7800_0000);
assert_eq!(reg[0].size::<u32>().unwrap(), 0x80_0000);
}
#[macro_export]
macro_rules! load_dtb_dts_pair {
($name:expr) => {
(
include_bytes!(concat!("dtb/", $name, ".dtb")).as_slice(),
include_str!(concat!("dts/", $name, ".dts")),
$name,
)
};
}
const ALL_DT_FILES: &[(&[u8], &str, &str)] = &[
load_dtb_dts_pair!("test_children_nested"),
load_dtb_dts_pair!("test_children"),
load_dtb_dts_pair!("test_memreserve"),
load_dtb_dts_pair!("test_pretty_print"),
load_dtb_dts_pair!("test_props"),
load_dtb_dts_pair!("test_traversal"),
load_dtb_dts_pair!("test"),
];
#[test]
fn pretty_print() {
for (dtb, expected_dts, name) in ALL_DT_FILES {
let fdt = Fdt::new(dtb).unwrap();
let s = fdt.to_string();
let expected = expected_dts
.replace("\r\n", "\n");
assert_eq!(s, expected, "Mismatch for {name}");
}
}
#[test]
#[cfg(feature = "write")]
fn round_trip() {
round_trip_impl(|dtb| Fdt::new(dtb).unwrap());
}
#[test]
#[cfg(feature = "write")]
fn round_trip_unchecked() {
round_trip_impl(|dtb| Fdt::new_unchecked(dtb));
}
#[test]
#[cfg(feature = "write")]
fn round_trip_raw() {
round_trip_impl(|dtb| unsafe { Fdt::from_raw(dtb.as_ptr()).unwrap() });
}
#[test]
#[cfg(feature = "write")]
fn round_trip_raw_unchecked() {
round_trip_impl(|dtb| unsafe { Fdt::from_raw_unchecked(dtb.as_ptr()) });
}
#[cfg(feature = "write")]
fn round_trip_impl(construct_fdt: impl Fn(&[u8]) -> Fdt) {
for (dtb, _dts, name) in ALL_DT_FILES {
let fdt = construct_fdt(*dtb);
let ir = DeviceTree::from_fdt(&fdt).unwrap();
let new_dtb = ir.to_dtb();
assert_eq!(dtb.to_vec(), new_dtb, "Mismatch for {name}");
}
}