fdt-edit is a pure-Rust, #![no_std] library for creating, loading, editing, querying, and re-encoding Flattened Device Tree (FDT) blobs.
The crate is intended for firmware, kernels, bootloaders, and embedded tooling that need a mutable in-memory device tree representation instead of a read-only parser.
- Parse existing DTB data into an editable arena-backed tree
- Build new device trees programmatically from scratch
- Add, update, and remove nodes and properties
- Query nodes by path, phandle, or compatible string
- Re-encode the edited tree back into DTB bytes
- Work in
no_stdenvironments withalloc
This repository originally focused on parsing. The current high-level crate is fdt-edit, which sits on top of fdt-raw and provides a mutable API for real tree manipulation.
Compared with a read-only parser, fdt-edit is designed for workflows such as:
- patching a board DTB before boot
- constructing a synthetic tree in tests
- rewriting properties like
reg,status,compatible, orinterrupt-parent - preserving memory reservation entries while round-tripping DTB data
Add the crate to your Cargo.toml:
[dependencies]
fdt-edit = "0.2.0"use fdt_edit::{Fdt, Node, Property};
# fn main() -> Result<(), fdt_edit::FdtError> {
let dtb: &[u8] = &[]; // replace with real DTB bytes
let mut fdt = Fdt::from_bytes(dtb)?;
let root_id = fdt.root_id();
fdt.node_mut(root_id)
.unwrap()
.set_property(Property::new("model", b"example-board\0".to_vec()));
let soc_id = if let Some(node) = fdt.get_by_path("/soc") {
node.id()
} else {
fdt.add_node(root_id, Node::new("soc"))
};
let mut uart = Node::new("uart@1000");
uart.set_property(Property::new("compatible", b"ns16550a\0".to_vec()));
uart.set_property(Property::new(
"reg",
vec![0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00],
));
fdt.add_node(soc_id, uart);
let encoded = fdt.encode();
assert!(!encoded.is_empty());
# Ok(())
# }use fdt_edit::{Fdt, Node, Property};
let mut fdt = Fdt::new();
let root_id = fdt.root_id();
fdt.node_mut(root_id).unwrap().set_property(Property::new(
"#address-cells",
2u32.to_be_bytes().to_vec(),
));
fdt.node_mut(root_id).unwrap().set_property(Property::new(
"#size-cells",
1u32.to_be_bytes().to_vec(),
));
let mut memory = Node::new("memory@80000000");
memory.set_property(Property::new("device_type", b"memory\0".to_vec()));
memory.set_property(Property::new(
"reg",
vec![
0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00,
],
));
fdt.add_node(root_id, memory);
let dtb = fdt.encode();
assert!(dtb.len() >= 40);use fdt_edit::{Fdt, NodeType};
# fn main() -> Result<(), fdt_edit::FdtError> {
# let dtb: &[u8] = &[]; // replace with real DTB bytes
let fdt = Fdt::from_bytes(dtb)?;
for node in fdt.find_compatible(&["pci-host-ecam-generic"]) {
if let NodeType::Pci(pci) = node {
let _bus_range = pci.bus_range();
let _interrupt_cells = pci.interrupt_cells();
}
}
# Ok(())
# }Fdt::new(): create an empty editable treeFdt::from_bytes(): parse DTB bytes into an editable treeFdt::from_ptr(): parse from a raw pointerroot_id(): get the root node IDnode()/node_mut(): access raw mutable nodes by IDadd_node(): insert a child noderemove_node()/remove_by_path(): delete nodes and subtreesget_by_path(): fetch a classified node view by absolute path or aliasget_by_phandle(): fetch a node by phandlefind_compatible(): search by compatible stringall_nodes(): depth-first iteration over the whole treeencode(): serialize the tree back into DTB bytes
Node::new(name): create a nodeset_property(): add or replace a propertyremove_property(): delete a propertyget_property(): inspect a propertychildren(): list child node IDs- helpers like
address_cells(),size_cells(),phandle(),compatible(),status()
Property::new(name, data): create a raw propertyget_u32()/get_u64(): decode integer valuesset_u32_ls()/set_u64(): encode integer valuesas_str()/as_str_iter(): decode string and string-list propertiesset_string()/set_string_ls(): update string data
get_by_path(), get_by_phandle(), and all_nodes() return classified node views, so code can branch on device-tree semantics instead of only raw node names.
Available typed views include:
NodeType::GenericNodeType::MemoryNodeType::InterruptControllerNodeType::ClockNodeType::Pci
These views expose helpers such as inherited interrupt-parent lookup, translated reg handling, clock metadata, memory region inspection, and PCI-specific range or interrupt-map parsing.
fdt-edit preserves the parts of the tree that matter for boot-time DTB generation:
- header metadata such as
boot_cpuid_phys - memory reservation entries
- node hierarchy and property ordering
- string table regeneration during encoding
The crate is built for parse-edit-encode workflows and includes tests that round-trip real DTBs from several platforms.
This repository is a small workspace:
fdt-edit: the high-level editable FDT library described in this READMEfdt-raw: lower-level parsing and data primitives used byfdt-editdtb-file: DTB fixtures used by tests and examples
cargo test -p fdt-editThe test suite covers:
- parsing real DTB fixtures
- tree traversal and path lookup
- typed node classification
- inherited interrupt-parent resolution
- DTB encoding and round-trip correctness
- memory reservation serialization
fdt-edit is licensed under MIT OR Apache-2.0.