# fdt-edit Project Context for LLMs
## Project Overview
fdt-edit is a high-level Rust library for creating, editing, and encoding Flattened Device Tree (FDT) structures. It is part of the fdt-parser workspace and provides comprehensive functionality for device tree manipulation with full `no_std` compatibility.
## Key Information
- **Project Name**: fdt-edit
- **Version**: 0.1.1
- **Language**: Rust (edition 2021)
- **License**: MIT OR Apache-2.0
- **Repository**: https://github.com/drivercraft/fdt-parser
- **Dependencies**: fdt-raw (low-level parsing), log, enum_dispatch
- **Target**: Embedded and no_std environments
- **Features**: Complete device tree parsing, editing, and encoding
## Architecture
### Core Components
1. **Fdt Structure** (`src/fdt.rs`) - Main device tree container
- Creates empty device trees: `Fdt::new()`
- Parses from raw DTB bytes: `Fdt::from_bytes(&dtb_data)`
- Parses from raw pointer: `unsafe { Fdt::from_ptr(ptr) }`
- Encodes back to DTB format: `fdt.encode()`
- Manages node tree, memory reservations, and phandle cache
- Provides Display trait for complete DTS output
- Overlay application support
- Alias resolution and phandle-based node lookup
2. **Node System** (`src/node/mod.rs`) - Hierarchical node structure
- Core `Node` struct with property and child management
- Node iteration and references (`NodeRef`, `NodeMut`, `NodeKind`)
- Generic node operations and context handling
- Formatted node display with configurable options
- Specialized node types: memory, clock, pci, interrupt controller
3. **Property System** (`src/prop/mod.rs`) - Device tree properties
- Type-safe property access (u32, u64, strings, byte arrays)
- Property value encoding/decoding with proper endianness
- Support for common property types (reg, ranges, compatible)
4. **Encoding System** (`src/encode.rs`) - DTB generation
- `FdtEncoder` converts in-memory structures to DTB format
- `FdtData` wrapper for encoded data with access methods
5. **Context Management** (`src/ctx.rs`) - Tree traversal context
- Maintains current path and inheritance context
- Manages `#address-cells`, `#size-cells` propagation
- Tracks interrupt parent relationships
## Key Data Types and Enums
```rust
// Core types
pub struct Fdt { /* device tree container */ }
pub struct Node { /* tree node with properties and children */ }
pub struct Property { /* device tree property */ }
// Node references (immutable)
pub enum NodeRef<'a> {
Generic(NodeRefGen<'a>),
Pci(NodeRefPci<'a>),
Clock(NodeRefClock<'a>),
InterruptController(NodeRefInterruptController<'a>),
Memory(NodeRefMemory<'a>),
}
// Node references (mutable)
pub enum NodeMut<'a> {
Generic(NodeMutGen<'a>),
}
// Node kind for pattern matching
pub enum NodeKind<'a> {
Generic(NodeRefGen<'a>),
Pci(NodeRefPci<'a>),
Clock(NodeRefClock<'a>),
InterruptController(NodeRefInterruptController<'a>),
Memory(NodeRefMemory<'a>),
}
// Supporting types
pub struct MemoryReservation { /* memory reservation entry */ }
pub struct Context<'a> { /* traversal context */ }
pub struct RegInfo { /* register information */ }
pub struct RangesEntry { /* address translation entry */ }
pub enum Status { /* node status (okay/disabled) */ }
```
## Complete API Reference
### Fdt Operations
#### Creation and Parsing
```rust
// Create empty device tree
let fdt = Fdt::new();
// Parse from DTB data
let fdt = Fdt::from_bytes(&dtb_data)?;
// Parse from pointer (unsafe)
let fdt = unsafe { Fdt::from_ptr(ptr)? };
```
#### Access and Modification
```rust
// Access root node
let root = fdt.root();
let mut root_mut = fdt.root_mut();
// Encode to DTB format
let fdt_data = fdt.encode();
// Display as DTS
println!("{}", fdt);
// Memory reservations
for reservation in &fdt.memory_reservations {
println!("Reserved: 0x{:x}-0x{:x}", reservation.address, reservation.address + reservation.size);
}
```
#### Overlay Support
```rust
// Basic overlay application
fdt.apply_overlay(&overlay_fdt)?;
// Overlay with delete support
fdt.apply_overlay_with_delete(&overlay, Some(delete_fdt))?;
```
#### Alias Resolution and phandle Management
```rust
// Get all aliases
for (alias, target) in fdt.aliases() {
println!("Alias: {} -> {}", alias, target);
}
// Resolve specific alias
if let Some(target) = fdt.resolve_alias("serial0") {
let node = fdt.get_by_path(target);
}
// Find nodes by phandle
let node = fdt.find_by_phandle(phandle)?;
let mut node_mut = fdt.find_by_phandle_mut(phandle)?;
// Rebuild phandle cache
fdt.rebuild_phandle_cache();
```
#### Node Navigation and Search
```rust
// Path-based access
let node = fdt.get_by_path("/chosen"); // Exact path match
let mut node_mut = fdt.get_by_path_mut("/soc"); // Mutable access
// Pattern-based search
let virtio_nodes: Vec<_> = fdt.find_by_path("/virtio_mmio").collect();
let compatible_nodes = fdt.find_compatible(&["vendor,device"]);
// Global iteration
for node in fdt.all_nodes() { /* iterate all nodes */ }
for node in fdt.all_nodes_mut() { /* mutable iteration */ }
```
### Node Manipulation
#### Creation and Structure
```rust
// Create nodes
let mut node = Node::new("test-device@12340000");
let node = Node::new("soc"); // Creates empty node
// Tree structure
node.add_child(child_node);
let removed_child = node.remove_child("child-name");
// Node hierarchy
let child = node.get_child("child-name");
let mut child_mut = node.get_child_mut("child-name");
for child in node.children() { /* iterate children */ }
for child in node.children_mut() { /* mutable iteration */ }
```
#### Property Management
```rust
// Property access
let prop = node.get_property("compatible");
let mut prop_mut = node.get_property_mut("reg");
let removed_prop = node.remove_property("status");
// Node properties
node.set_property(Property::new("compatible", b"vendor,test\0"));
node.set_property(Property::new("reg", &[0x12340000u32, 0x1000u32]));
```
### Property Operations
#### Creation and Access
```rust
// Create properties
let prop = Property::new("reg", data); // From raw bytes
let prop = Property::new("compatible", b"vendor,test\0");
// Type-safe access
let value = prop.get_u32()?; // Get 32-bit value
let values = prop.get_u32_iter().collect::<Vec<_>>(); // Multiple u32 values
let value = prop.get_u64()?; // Get 64-bit value
let string = prop.as_str()?; // Get string
let strings = prop.as_str_iter().collect::<Vec<_>>(); // Multiple strings
let reader = prop.as_reader(); // Raw data access
```
#### Property Modification
```rust
// Set property values
prop.set_u32_ls(&[0x12345678]); // Set multiple u32 values (little-endian)
prop.set_u64(0x123456789abcdef0);
prop.set_string("test-value");
prop.set_string_ls(&["value1", "value2"]);
```
### Specialized Node Operations
#### Memory Nodes
```rust
for node in fdt.all_nodes() {
if let NodeKind::Memory(mem) = node.as_ref() {
println!("Memory node: {}", mem.name());
for region in mem.regions() {
println!(" Region: 0x{:x}-0x{:x}", region.address, region.address + region.size);
}
if let Some(device_type) = mem.device_type() {
println!(" Type: {}", device_type);
}
}
}
```
#### Clock Nodes
```rust
for node in fdt.all_nodes() {
if let NodeKind::Clock(clock) = node.as_ref() {
println!("Clock: {}", clock.name());
println!(" #clock-cells: {}", clock.clock_cells);
// Clock output names
for (i, name) in clock.clock_output_names.iter().enumerate() {
println!(" Output {}: {}", i, name);
}
// Clock references
for clock_ref in clock.clocks() {
println!(" Clock ref: phandle={}, cells={}", clock_ref.phandle, clock_ref.cells);
}
}
}
```
#### PCI Nodes
```rust
for node in fdt.all_nodes() {
if let NodeKind::Pci(pci) = node.as_ref() {
println!("PCI node: {}", pci.name());
// Bus range
if let Some(range) = pci.bus_range() {
println!(" Bus range: {}-{}", range.start, range.end);
}
// Address ranges
if let Some(ranges) = pci.ranges() {
for range in ranges {
println!(" Range: {:x?}", range);
}
}
// Interrupt mapping
if let Ok(interrupt_map) = pci.interrupt_map() {
for entry in interrupt_map {
println!(" Interrupt map: {:x?}", entry);
}
}
}
}
```
#### Interrupt Controllers
```rust
for node in fdt.all_nodes() {
if let NodeKind::InterruptController(ic) = node.as_ref() {
println!("Interrupt controller: {}", ic.name());
if let Some(cells) = ic.interrupt_cells() {
println!(" #interrupt-cells: {}", cells);
}
if let Some(addr_cells) = ic.interrupt_address_cells() {
println!(" #address-cells: {}", addr_cells);
}
println!(" Is controller: {}", ic.is_interrupt_controller());
for compatible in ic.compatibles() {
println!(" Compatible: {}", compatible);
}
}
}
```
### Register and Range Operations
```rust
// Reg property access
for node in fdt.all_nodes() {
if let Some(regs) = node.regs() {
for reg in regs {
println!("Reg: addr=0x{:x}, child_addr=0x{:x}, size={:?}",
reg.address, reg.child_bus_address, reg.size);
}
}
}
// Ranges property (address translation)
for node in fdt.all_nodes() {
if let Some(ranges) = node.ranges(node.address_cells().unwrap_or(2)) {
for range in ranges {
println!("Range: child={:x}-{:x}, parent={:x}-{:x}, size={:x}",
range.child_bus_address, range.child_bus_address + range.size,
range.parent_bus_address, range.parent_bus_address + range.size,
range.size);
}
}
}
```
### Display and Formatting
```rust
// FDT display (complete DTS)
println!("{}", fdt);
// Node display with options
let display = NodeDisplay::new(&node)
.indent(4)
.show_address(true)
.show_size(true);
println!("{}", display);
// NodeRef display
let ref_display = NodeRefDisplay::new(&node_ref)
.indent(2)
.show_details(true);
println!("{}", ref_display);
```
## Testing Framework
### Test Organization
- `tests/edit.rs` - Round-trip parsing and encoding validation
- `tests/memory.rs` - Memory node functionality
- `tests/clock.rs` - Clock node detection and properties
- `tests/pci.rs` - PCI node operations
- `tests/irq.rs` - Interrupt controller handling
- `tests/range.rs` - Register and range property testing
- `tests/remove_node.rs` - Node removal operations
- `tests/display_debug.rs` - Display and debug formatting
- `tests/find2.rs` - Advanced search operations
### Test Data Sources
- QEMU device trees (ARM virtio)
- Raspberry Pi 4B device trees
- Phytium platform device trees
- Custom test device trees
### Test Validation Methods
- Round-trip compatibility (parse → modify → encode → compare)
- DTC tool comparison (`dtc -I dtb -O dts`)
- Property value validation
- Tree structure integrity checks
## Error Handling
The library uses `FdtError` for comprehensive error reporting:
- Parse errors from invalid DTB data
- Node path resolution failures
- Property access errors
- Encoding validation errors
## Development and Testing
### Build Commands
```bash
# Build library
cargo build -p fdt-edit
# Run all tests
cargo test -p fdt-edit
# Format code
cargo fmt -p fdt-edit
# Run clippy
cargo clippy -p fdt-edit
```
### Test Execution
```bash
# Run specific test
cargo test -p fdt-edit test_memory_node_detection
# Run tests with logging
RUST_LOG=debug cargo test -p fdt-edit
# Round-trip test validation
cargo test -p fdt-edit test_parse_and_rebuild
```
## Integration Examples
### Complete Device Tree Processing
```rust
use fdt_edit::*;
// Load and parse
let dtb_data = std::fs::read("device.dtb")?;
let mut fdt = Fdt::from_bytes(&dtb_data)?;
// Find and modify memory nodes
for node in fdt.all_nodes_mut() {
if let NodeMut::Generic(mut node) = node {
// Check if this is a memory node
if let Some(device_type) = node.get_property("device_type") {
if let Some("memory") = device_type.as_str() {
// Modify memory properties
}
}
}
}
// Add new node
let mut new_node = Node::new("test-device");
new_node.set_property(Property::new("compatible", b"vendor,test\0"));
new_node.set_property(Property::new("reg", &[0x12340000u32, 0x1000u32]));
fdt.root_mut().add_child(new_node);
// Apply overlay
let overlay_fdt = Fdt::from_bytes(&overlay_data)?;
fdt.apply_overlay(&overlay_fdt)?;
// Save modified device tree
let modified_dtb = fdt.encode();
std::fs::write("modified.dtb", modified_dtb.as_bytes())?;
// Export as DTS
std::fs::write("modified.dts", format!("{}", fdt));
```
## Limitations and Constraints
### Current Limitations
- Property editing APIs are partially implemented
- Some specialized node types may need additional features
- Large device trees may have memory constraints in `no_std` environments
### Platform Considerations
- Designed for both `std` and `no_std` environments
- Uses `alloc` crate for dynamic memory when available
- Endianness handling for cross-platform compatibility
## Future Development Roadmap
### Planned Features
- Complete property CRUD operations
- Enhanced node manipulation APIs
- Additional specialized node types
- Performance optimizations for large trees
- Streaming parsing support for very large device trees
- Validation and linting tools