') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - drivercraft/fdt-parser: FDT parser · GitHub
Skip to content

Repository files navigation

fdt-edit

Crates.ioDocumentation

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.

What It Does

  • 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_std environments with alloc

Why fdt-edit

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, or interrupt-parent
  • preserving memory reservation entries while round-tripping DTB data

Installation

Add the crate to your Cargo.toml:

[dependencies]
fdt-edit = "0.2.0"

Quick Start

Load, Modify, Encode

use fdt_edit::{Fdt,Node,Property};
# fnmain() -> Result<(), fdt_edit::FdtError>{let dtb:&[u8] = &[];// replace with real DTB bytesletmut 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 = ifletSome(node) = fdt.get_by_path("/soc"){
node.id()}else{
fdt.add_node(root_id,Node::new("soc"))};letmut 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(())
# }

Build A New Tree

use fdt_edit::{Fdt,Node,Property};letmut 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(),));letmut 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);

Query Typed Nodes

use fdt_edit::{Fdt,NodeType};
# fnmain() -> Result<(), fdt_edit::FdtError>{
# let dtb:&[u8] = &[];// replace with real DTB byteslet fdt = Fdt::from_bytes(dtb)?;for node in fdt.find_compatible(&["pci-host-ecam-generic"]){ifletNodeType::Pci(pci) = node {let _bus_range = pci.bus_range();let _interrupt_cells = pci.interrupt_cells();}}
# Ok(())
# }

Core API

Fdt

  • Fdt::new(): create an empty editable tree
  • Fdt::from_bytes(): parse DTB bytes into an editable tree
  • Fdt::from_ptr(): parse from a raw pointer
  • root_id(): get the root node ID
  • node() / node_mut(): access raw mutable nodes by ID
  • add_node(): insert a child node
  • remove_node() / remove_by_path(): delete nodes and subtrees
  • get_by_path(): fetch a classified node view by absolute path or alias
  • get_by_phandle(): fetch a node by phandle
  • find_compatible(): search by compatible string
  • all_nodes(): depth-first iteration over the whole tree
  • encode(): serialize the tree back into DTB bytes

Node

  • Node::new(name): create a node
  • set_property(): add or replace a property
  • remove_property(): delete a property
  • get_property(): inspect a property
  • children(): list child node IDs
  • helpers like address_cells(), size_cells(), phandle(), compatible(), status()

Property

  • Property::new(name, data): create a raw property
  • get_u32() / get_u64(): decode integer values
  • set_u32_ls() / set_u64(): encode integer values
  • as_str() / as_str_iter(): decode string and string-list properties
  • set_string() / set_string_ls(): update string data

Typed Node Views

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::Generic
  • NodeType::Memory
  • NodeType::InterruptController
  • NodeType::Clock
  • NodeType::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.

Encoding And Round-Tripping

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.

Repository Layout

This repository is a small workspace:

  • fdt-edit: the high-level editable FDT library described in this README
  • fdt-raw: lower-level parsing and data primitives used by fdt-edit
  • dtb-file: DTB fixtures used by tests and examples

Testing

cargo test -p fdt-edit

The 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

License

fdt-edit is licensed under MIT OR Apache-2.0.

Repository

https://github.com/drivercraft/fdt-parser

About

FDT parser

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages