use std::collections::HashMap;
const FDT_MAGIC: u32 = 0xd00d_feed;
const FDT_BEGIN_NODE: u32 = 0x0000_0001;
const FDT_END_NODE: u32 = 0x0000_0002;
const FDT_PROP: u32 = 0x0000_0003;
const FDT_END: u32 = 0x0000_0009;
const FDT_VERSION: u32 = 17;
const FDT_LAST_COMP_VERSION: u32 = 16;
const FDT_HEADER_SIZE: u32 = 40;
const FDT_RSVMAP_SIZE: u32 = 16;
pub struct FdtBuilder {
strings: Vec<u8>,
struct_buf: Vec<u8>,
string_offsets: HashMap<String, u32>,
}
impl FdtBuilder {
pub fn new() -> Self {
Self {
strings: Vec::new(),
struct_buf: Vec::new(),
string_offsets: HashMap::new(),
}
}
pub fn begin_node(&mut self, name: &str) {
self.push_u32(FDT_BEGIN_NODE);
self.push_str(name);
}
pub fn end_node(&mut self) {
self.push_u32(FDT_END_NODE);
}
pub fn property_u32(&mut self, name: &str, val: u32) {
let data = val.to_be_bytes();
self.property_bytes(name, &data);
}
pub fn property_u64(&mut self, name: &str, val: u64) {
let data = val.to_be_bytes();
self.property_bytes(name, &data);
}
pub fn property_string(&mut self, name: &str, val: &str) {
let mut data = val.as_bytes().to_vec();
data.push(0); self.property_bytes(name, &data);
}
pub fn property_u32_list(&mut self, name: &str, vals: &[u32]) {
let mut data = Vec::with_capacity(vals.len() * 4);
for &v in vals {
data.extend_from_slice(&v.to_be_bytes());
}
self.property_bytes(name, &data);
}
pub fn property_bytes(&mut self, name: &str, data: &[u8]) {
let nameoff = self.intern_string(name);
self.push_u32(FDT_PROP);
self.push_u32(data.len() as u32);
self.push_u32(nameoff);
self.struct_buf.extend_from_slice(data);
self.align4();
}
pub fn finish(mut self) -> Vec<u8> {
self.push_u32(FDT_END);
let off_mem_rsvmap = FDT_HEADER_SIZE;
let off_dt_struct = off_mem_rsvmap + FDT_RSVMAP_SIZE;
let off_dt_strings = off_dt_struct + self.struct_buf.len() as u32;
let totalsize = off_dt_strings + self.strings.len() as u32;
let mut blob = Vec::with_capacity(totalsize as usize);
blob.extend_from_slice(&FDT_MAGIC.to_be_bytes());
blob.extend_from_slice(&totalsize.to_be_bytes());
blob.extend_from_slice(&off_dt_struct.to_be_bytes());
blob.extend_from_slice(&off_dt_strings.to_be_bytes());
blob.extend_from_slice(&off_mem_rsvmap.to_be_bytes());
blob.extend_from_slice(&FDT_VERSION.to_be_bytes());
blob.extend_from_slice(&FDT_LAST_COMP_VERSION.to_be_bytes());
blob.extend_from_slice(&0u32.to_be_bytes());
blob.extend_from_slice(&(self.strings.len() as u32).to_be_bytes());
blob.extend_from_slice(&(self.struct_buf.len() as u32).to_be_bytes());
blob.extend_from_slice(&[0u8; 16]);
blob.extend_from_slice(&self.struct_buf);
blob.extend_from_slice(&self.strings);
blob
}
fn push_u32(&mut self, val: u32) {
self.struct_buf.extend_from_slice(&val.to_be_bytes());
}
fn push_str(&mut self, s: &str) {
self.struct_buf.extend_from_slice(s.as_bytes());
self.struct_buf.push(0); self.align4();
}
fn align4(&mut self) {
let rem = self.struct_buf.len() % 4;
if rem != 0 {
let pad = 4 - rem;
self.struct_buf.extend(std::iter::repeat_n(0u8, pad));
}
}
fn intern_string(&mut self, name: &str) -> u32 {
if let Some(&off) = self.string_offsets.get(name) {
return off;
}
let off = self.strings.len() as u32;
self.strings.extend_from_slice(name.as_bytes());
self.strings.push(0);
self.string_offsets.insert(name.to_string(), off);
off
}
}
impl Default for FdtBuilder {
fn default() -> Self {
Self::new()
}
}