blob: 11b76bc2d0c690e779360755982dc23edf261d25 [file] [log] [blame]
// Copyright 2018 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! This module writes Flattened Devicetree blobs as defined here:
//! <https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html>
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::io;
use indexmap::map::Entry;
use indexmap::IndexMap;
use remain::sorted;
use thiserror::Error as ThisError;
use crate::path::Path;
use crate::propval::FromFdtPropval;
use crate::propval::ToFdtPropval;
pub(crate) const SIZE_U32: usize = std::mem::size_of::<u32>();
pub(crate) const SIZE_U64: usize = std::mem::size_of::<u64>();
#[sorted]
#[derive(ThisError, Debug)]
pub enum Error {
#[error("Error applying device tree overlay: {}", .0)]
ApplyOverlayError(String),
#[error("Binary size must fit in 32 bits")]
BinarySizeTooLarge,
#[error("Duplicate node {}", .0)]
DuplicateNode(String),
#[error("I/O error dumping FDT to file code={} path={}", .0, .1.display())]
FdtDumpIoError(io::Error, std::path::PathBuf),
#[error("Error writing FDT to guest memory")]
FdtGuestMemoryWriteError,
#[error("I/O error code={0}")]
FdtIoError(io::Error),
#[error("Parse error reading FDT parameters: {}", .0)]
FdtParseError(String),
#[error("Error applying FDT tree filter: {}", .0)]
FilterError(String),
#[error("Invalid name string: {}", .0)]
InvalidName(String),
#[error("Invalid path: {}", .0)]
InvalidPath(String),
#[error("Invalid string value {}", .0)]
InvalidString(String),
#[error("Expected phandle value for IOMMU of type: {}, id: {:?}", .0, .1)]
MissingIommuPhandle(String, Option<u32>),
#[error("Property value is not valid")]
PropertyValueInvalid,
#[error("Property value size must fit in 32 bits")]
PropertyValueTooLarge,
#[error("Total size must fit in 32 bits")]
TotalSizeTooLarge,
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::FdtIoError(value)
}
}
pub type Result<T> = std::result::Result<T, Error>;
type Blob<'a> = &'a [u8];
const FDT_BEGIN_NODE: u32 = 0x00000001;
const FDT_END_NODE: u32 = 0x00000002;
const FDT_PROP: u32 = 0x00000003;
const FDT_NOP: u32 = 0x00000004;
const FDT_END: u32 = 0x00000009;
// Consume and return `n` bytes from the beginning of a slice.
fn consume<'a>(bytes: &mut &'a [u8], n: usize) -> Result<&'a [u8]> {
let mid = n;
if mid > bytes.len() {
Err(Error::PropertyValueInvalid)
} else {
let (data_bytes, rest) = bytes.split_at(n);
*(bytes) = rest;
Ok(data_bytes)
}
}
// Consume a u32 from a byte slice.
#[inline]
fn rdu32(data: &mut Blob) -> Result<u32> {
Ok(u32::from_be_bytes(
// Unwrap won't panic because the slice length is checked in consume().
consume(data, SIZE_U32)?.try_into().unwrap(),
))
}
// Consume a u64 from a byte slice.
#[inline]
fn rdu64(data: &mut Blob) -> Result<u64> {
Ok(u64::from_be_bytes(
// Unwrap won't panic because the slice length is checked in consume().
consume(data, SIZE_U64)?.try_into().unwrap(),
))
}
// Return the number of padding bytes required to align `size` to `alignment`.
#[inline]
fn align_pad_len(size: usize, alignment: usize) -> usize {
(alignment - size % alignment) % alignment
}
// Pad a byte vector to given alignment.
#[inline]
fn align_data(data: &mut Vec<u8>, alignment: usize) {
data.resize(align_pad_len(data.len(), alignment) + data.len(), 0u8);
}
// Construct a string from the start of a byte slice until the first null byte.
pub(crate) fn c_str_to_string(input: Blob) -> Option<String> {
let size = input.iter().position(|&v| v == 0u8)?;
String::from_utf8(input[..size].to_vec()).ok()
}
// Verify FDT property name.
fn is_valid_prop_name(name: &str) -> bool {
const ALLOWED_SPECIAL_CHARS: [u8; 7] = [b'.', b',', b'_', b'+', b'?', b'#', b'-'];
name.bytes()
.all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
}
// Verify FDT node name.
fn is_valid_node_name(name: &str) -> bool {
const ALLOWED_SPECIAL_CHARS: [u8; 6] = [b'.', b',', b'_', b'+', b'-', b'@'];
const ADDR_SEP: u8 = b'@';
// At most one `@` separating node-name and unit-address
if name.bytes().filter(|&c| c == ADDR_SEP).count() > 1 {
return false;
}
name.bytes()
.all(|c| c.is_ascii_alphanumeric() || ALLOWED_SPECIAL_CHARS.contains(&c))
}
// An implementation of FDT header.
#[derive(Default, Debug)]
struct FdtHeader {
magic: u32, // magic word
total_size: u32, // total size of DT block
off_dt_struct: u32, // offset to structure
off_dt_strings: u32, // offset to strings
off_mem_rsvmap: u32, // offset to memory reserve map
version: u32, // format version
last_comp_version: u32, // last compatible version
boot_cpuid_phys: u32, // Which physical CPU id we're booting on
size_dt_strings: u32, // size of the strings block
size_dt_struct: u32, // size of the structure block
}
impl FdtHeader {
const MAGIC: u32 = 0xd00dfeed;
const VERSION: u32 = 17;
const LAST_COMP_VERSION: u32 = 16;
const SIZE: usize = 10 * SIZE_U32;
// Create a new FdtHeader instance.
fn new(
total_size: u32,
off_dt_struct: u32,
off_dt_strings: u32,
off_mem_rsvmap: u32,
boot_cpuid_phys: u32,
size_dt_strings: u32,
size_dt_struct: u32,
) -> Self {
Self {
magic: Self::MAGIC,
total_size,
off_dt_struct,
off_dt_strings,
off_mem_rsvmap,
version: Self::VERSION,
last_comp_version: Self::LAST_COMP_VERSION,
boot_cpuid_phys,
size_dt_strings,
size_dt_struct,
}
}
// Dump FDT header to a byte vector.
fn write_blob(&self, buffer: &mut [u8]) -> Result<()> {
assert_eq!(buffer.len(), Self::SIZE);
for (chunk, val_u32) in buffer.chunks_exact_mut(SIZE_U32).zip(&[
self.magic,
self.total_size,
self.off_dt_struct,
self.off_dt_strings,
self.off_mem_rsvmap,
self.version,
self.last_comp_version,
self.boot_cpuid_phys,
self.size_dt_strings,
self.size_dt_struct,
]) {
chunk.copy_from_slice(&val_u32.to_be_bytes());
}
Ok(())
}
// Load FDT header from a byte slice.
fn from_blob(mut input: Blob) -> Result<Self> {
if input.len() < Self::SIZE {
return Err(Error::FdtParseError("invalid binary size".into()));
}
let input = &mut input;
let header = Self {
magic: rdu32(input)?,
total_size: rdu32(input)?,
off_dt_struct: rdu32(input)?,
off_dt_strings: rdu32(input)?,
off_mem_rsvmap: rdu32(input)?,
version: rdu32(input)?,
last_comp_version: rdu32(input)?,
boot_cpuid_phys: rdu32(input)?,
size_dt_strings: rdu32(input)?,
size_dt_struct: rdu32(input)?,
};
if header.magic != Self::MAGIC {
return Err(Error::FdtParseError("invalid header magic".into()));
}
if header.version < Self::VERSION {
return Err(Error::FdtParseError("unsupported FDT version".into()));
}
if header.off_mem_rsvmap >= header.off_dt_strings
|| header.off_mem_rsvmap < FdtHeader::SIZE as u32
{
return Err(Error::FdtParseError(
"invalid reserved memory offset".into(),
));
}
let off_dt_struct_end = header
.off_dt_struct
.checked_add(header.size_dt_struct)
.ok_or_else(|| Error::FdtParseError("struct end offset must fit in 32 bits".into()))?;
if off_dt_struct_end > header.off_dt_strings {
return Err(Error::FdtParseError("struct and strings overlap".into()));
}
let off_dt_strings_end = header
.off_dt_strings
.checked_add(header.size_dt_strings)
.ok_or_else(|| Error::FdtParseError("strings end offset must fit in 32 bits".into()))?;
if off_dt_strings_end > header.total_size {
return Err(Error::FdtParseError("strings data past total size".into()));
}
Ok(header)
}
}
// An implementation of FDT strings block (property names)
#[derive(Default)]
struct FdtStrings {
strings: Vec<u8>,
string_offsets: BTreeMap<String, u32>,
}
impl FdtStrings {
// Load the strings block from a byte slice.
fn from_blob(input: Blob) -> Result<Self> {
if input.last().is_some_and(|i| *i != 0) {
return Err(Error::FdtParseError(
"strings block missing null terminator".into(),
));
}
let mut string_offsets = BTreeMap::new();
let mut offset = 0u32;
for bytes in input.split(|&x| x == 0u8) {
if bytes.is_empty() {
break;
}
let string = String::from_utf8(bytes.to_vec())
.map_err(|_| Error::FdtParseError("invalid value in strings block".into()))?;
string_offsets.insert(string, offset);
offset += u32::try_from(bytes.len() + 1).map_err(|_| Error::BinarySizeTooLarge)?;
}
Ok(Self {
strings: input.to_vec(),
string_offsets,
})
}
// Find an existing instance of a string `s`, or add it to the strings block.
// Returns the offset into the strings block.
fn intern_string(&mut self, s: &str) -> u32 {
if let Some(off) = self.string_offsets.get(s) {
*off
} else {
let off = self.strings.len() as u32;
self.strings.extend_from_slice(s.as_bytes());
self.strings.push(0u8);
self.string_offsets.insert(s.to_owned(), off);
off
}
}
// Write the strings blob to a `Write` object.
fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
Ok(writer.write_all(&self.strings)?)
}
// Return the string at given offset or `None` if such a string doesn't exist.
fn at_offset(&self, off: u32) -> Option<String> {
self.strings
.get(off as usize..)
.and_then(c_str_to_string)
.filter(|s| !s.is_empty())
}
}
/// Flattened device tree node.
///
/// This represents a single node from the FDT structure block. Every node may contain properties
/// and other (child) nodes.
#[derive(Debug, Clone)]
pub struct FdtNode {
/// Node name
pub(crate) name: String,
pub(crate) props: IndexMap<String, Vec<u8>>,
pub(crate) subnodes: IndexMap<String, FdtNode>,
}
impl FdtNode {
// Create a new node with the given name, properties, and child nodes. Return an error if
// node or property names do not satisfy devicetree naming criteria.
pub(crate) fn new(
name: String,
props: IndexMap<String, Vec<u8>>,
subnodes: IndexMap<String, FdtNode>,
) -> Result<Self> {
if !is_valid_node_name(&name) {
return Err(Error::InvalidName(name));
}
for pname in props.keys() {
if !is_valid_prop_name(pname) {
return Err(Error::InvalidName(pname.into()));
}
}
Ok(Self {
name,
props,
subnodes,
})
}
// Create an empty node with the given name.
pub(crate) fn empty(name: impl Into<String>) -> Result<Self> {
FdtNode::new(name.into(), [].into(), [].into())
}
fn read_token(input: &mut Blob) -> Result<u32> {
loop {
let value = rdu32(input)?;
if value != FDT_NOP {
return Ok(value);
}
}
}
// Parse binary content of an FDT node.
fn parse_node(input: &mut Blob, strings: &FdtStrings) -> Result<Self> {
// Node name
let name = c_str_to_string(input)
.ok_or_else(|| Error::FdtParseError("could not parse node name".into()))?;
let name_nbytes = name.len() + 1;
consume(input, name_nbytes + align_pad_len(name_nbytes, SIZE_U32))?;
// Node properties and subnodes
let mut props = IndexMap::new();
let mut subnodes = IndexMap::new();
let mut encountered_subnode = false; // Properties must appear before subnodes
loop {
match Self::read_token(input)? {
FDT_BEGIN_NODE => {
encountered_subnode = true;
let subnode = Self::parse_node(input, strings)?;
match subnodes.entry(subnode.name.clone()) {
Entry::Vacant(e) => e.insert(subnode),
Entry::Occupied(_) => return Err(Error::DuplicateNode(subnode.name)),
};
}
FDT_END_NODE => break,
FDT_PROP => {
if encountered_subnode {
return Err(Error::FdtParseError(
"unexpected prop token after subnode".into(),
));
}
let prop_len = rdu32(input)? as usize;
let prop_name_offset = rdu32(input)?;
let prop_blob = consume(input, prop_len + align_pad_len(prop_len, SIZE_U32))?;
let prop_name = strings.at_offset(prop_name_offset).ok_or_else(|| {
Error::FdtParseError(format!(
"invalid property name at {prop_name_offset:#x}",
))
})?;
// Keep the original (non-aligned) size as property value
props.insert(prop_name, prop_blob[..prop_len].to_vec());
}
FDT_NOP => continue,
FDT_END => return Err(Error::FdtParseError("unexpected END token".into())),
t => return Err(Error::FdtParseError(format!("invalid FDT token {t}"))),
}
}
FdtNode::new(name, props, subnodes)
}
// Load an `FdtNode` instance from a slice of bytes.
fn from_blob(mut input: Blob, strings: &FdtStrings) -> Result<Self> {
let input = &mut input;
if Self::read_token(input)? != FDT_BEGIN_NODE {
return Err(Error::FdtParseError("expected begin node token".into()));
}
let root = Self::parse_node(input, strings)?;
if Self::read_token(input)? != FDT_END {
Err(Error::FdtParseError("expected end node token".into()))
} else {
Ok(root)
}
}
// Write binary contents of a node to a vector of bytes.
fn write_blob(&self, writer: &mut impl io::Write, strings: &mut FdtStrings) -> Result<()> {
// Token
writer.write_all(&FDT_BEGIN_NODE.to_be_bytes())?;
// Name
writer.write_all(self.name.as_bytes())?;
writer.write_all(&[0])?; // Node name terminator
let pad_len = align_pad_len(self.name.len() + 1, SIZE_U32);
writer.write_all(&vec![0; pad_len])?;
// Properties
for (propname, propblob) in self.props.iter() {
// Prop token
writer.write_all(&FDT_PROP.to_be_bytes())?;
// Prop size
writer.write_all(&(propblob.len() as u32).to_be_bytes())?;
// Prop name offset
writer.write_all(&strings.intern_string(propname).to_be_bytes())?;
// Prop value
writer.write_all(propblob)?;
let pad_len = align_pad_len(propblob.len(), SIZE_U32);
writer.write_all(&vec![0; pad_len])?;
}
// Subnodes
for subnode in self.subnodes.values() {
subnode.write_blob(writer, strings)?;
}
// Token
writer.write_all(&FDT_END_NODE.to_be_bytes())?;
Ok(())
}
// Iterate over property names defined for this node.
pub(crate) fn prop_names(&self) -> impl std::iter::Iterator<Item = &str> {
self.props.keys().map(|s| s.as_str())
}
// Return true if a property with the given name exists.
pub(crate) fn has_prop(&self, name: &str) -> bool {
self.props.contains_key(name)
}
/// Read property value if it exists.
///
/// # Arguments
///
/// `name` - name of the property.
pub fn get_prop<T>(&self, name: &str) -> Option<T>
where
T: FromFdtPropval,
{
T::from_propval(self.props.get(name)?.as_slice())
}
// Read a phandle value (a `u32`) at some offset within a property value.
// Returns `None` if a phandle value cannot be constructed.
pub(crate) fn phandle_at_offset(&self, name: &str, offset: usize) -> Option<u32> {
let data = self.props.get(name)?;
data.get(offset..offset + SIZE_U32)
.and_then(u32::from_propval)
}
// Overwrite a phandle value (a `u32`) at some offset within a property value.
// Returns `Err` if the property doesn't exist, or if the property value is too short to
// construct a `u32` at given offset. Does not change property value size.
pub(crate) fn update_phandle_at_offset(
&mut self,
name: &str,
offset: usize,
phandle: u32,
) -> Result<()> {
let propval = self
.props
.get_mut(name)
.ok_or_else(|| Error::InvalidName(format!("property {name} does not exist")))?;
if let Some(bytes) = propval.get_mut(offset..offset + SIZE_U32) {
bytes.copy_from_slice(phandle.to_propval()?.as_slice());
Ok(())
} else {
Err(Error::PropertyValueInvalid)
}
}
/// Write a property.
///
/// # Arguments
///
/// `name` - name of the property; must be a valid property name according to DT spec.
/// `val` - value of the property (raw byte array).
pub fn set_prop<T>(&mut self, name: &str, value: T) -> Result<()>
where
T: ToFdtPropval,
{
if !is_valid_prop_name(name) {
return Err(Error::InvalidName(name.into()));
}
let bytes = value.to_propval()?;
// FDT property byte size must fit into a u32.
u32::try_from(bytes.len()).map_err(|_| Error::PropertyValueTooLarge)?;
self.props.insert(name.into(), bytes);
Ok(())
}
/// Return a reference to an existing subnode with given name, or `None` if it doesn't exist.
///
/// # Arguments
///
/// `name` - name of the node.
pub fn subnode(&self, name: &str) -> Option<&FdtNode> {
self.subnodes.get(name)
}
/// Create a node if it doesn't already exist, and return a mutable reference to it. Return
/// an error if the node name is not valid.
///
/// # Arguments
///
/// `name` - name of the node; must be a valid node name according to DT specification.
pub fn subnode_mut(&mut self, name: &str) -> Result<&mut FdtNode> {
if !self.subnodes.contains_key(name) {
self.subnodes.insert(name.into(), FdtNode::empty(name)?);
}
Ok(self.subnodes.get_mut(name).unwrap())
}
// Iterate subnode references.
pub(crate) fn iter_subnodes(&self) -> impl std::iter::Iterator<Item = &FdtNode> {
self.subnodes.values()
}
// Iterate mutable subnode references.
pub(crate) fn iter_subnodes_mut(&mut self) -> impl std::iter::Iterator<Item = &mut FdtNode> {
self.subnodes.values_mut()
}
}
/// Interface for creating and manipulating a Flattened Devicetree (FDT) and emitting
/// a Devicetree Blob (DTB).
///
/// # Example
///
/// ```rust
/// use cros_fdt::Fdt;
///
/// # fn main() -> cros_fdt::Result<()> {
/// let mut fdt = Fdt::new(&[]);
/// let root_node = fdt.root_mut();
/// root_node.set_prop("compatible", "linux,dummy-virt")?;
/// root_node.set_prop("#address-cells", 0x2u32)?;
/// root_node.set_prop("#size-cells", 0x2u32)?;
/// let chosen_node = root_node.subnode_mut("chosen")?;
/// chosen_node.set_prop("linux,pci-probe-only", 1u32)?;
/// chosen_node.set_prop("bootargs", "panic=-1 console=hvc0 root=/dev/vda")?;
/// let dtb = fdt.finish().unwrap();
/// # Ok(())
/// # }
/// ```
pub struct Fdt {
pub(crate) reserved_memory: Vec<FdtReserveEntry>,
pub(crate) root: FdtNode,
strings: FdtStrings,
boot_cpuid_phys: u32,
}
/// Reserved physical memory region.
///
/// This represents an area of physical memory reserved by the firmware and unusable by the OS.
/// For example, this could be used to preserve bootloader code or data used at runtime.
#[derive(Clone, PartialEq, Debug)]
pub struct FdtReserveEntry {
/// Physical address of the beginning of the reserved region.
pub address: u64,
/// Size of the reserved region in bytes.
pub size: u64,
}
// Last entry in the reserved memory section
const RESVMEM_TERMINATOR: FdtReserveEntry = FdtReserveEntry::new(0, 0);
impl FdtReserveEntry {
/// Create a new FdtReserveEntry
///
/// # Arguments
///
/// `address` - start of reserved memory region.
/// `size` - size of reserved memory region.
pub const fn new(address: u64, size: u64) -> Self {
Self { address, size }
}
// Load a reserved memory entry from a byte slice.
fn from_blob(input: &mut Blob) -> Result<Self> {
Ok(Self {
address: rdu64(input)?,
size: rdu64(input)?,
})
}
// Dump the entry as a vector of bytes.
fn write_blob(&self, mut writer: impl io::Write) -> Result<()> {
writer.write_all(&self.address.to_be_bytes())?;
writer.write_all(&self.size.to_be_bytes())?;
Ok(())
}
}
impl Fdt {
/// Create a new flattened device tree instance with an initialized root node.
///
/// # Arguments
///
/// `mem_reservations` - reserved physical memory regions to list in the FDT header.
pub fn new(mem_reservations: &[FdtReserveEntry]) -> Self {
Self {
reserved_memory: mem_reservations.to_vec(),
root: FdtNode::empty("").unwrap(),
strings: FdtStrings::default(),
boot_cpuid_phys: 0u32,
}
}
/// Set the `boot_cpuid_phys` field of the devicetree header.
///
/// # Arguments
///
/// `boot_cpuid_phys` - CPU ID
pub fn set_boot_cpuid_phys(&mut self, boot_cpuid_phys: u32) {
self.boot_cpuid_phys = boot_cpuid_phys;
}
// Parse the reserved memory block from a binary blob.
fn parse_reserved_memory(mut input: Blob) -> Result<Vec<FdtReserveEntry>> {
let mut entries = vec![];
let input = &mut input;
loop {
let entry = FdtReserveEntry::from_blob(input)?;
if entry == RESVMEM_TERMINATOR {
break;
}
entries.push(entry);
}
Ok(entries)
}
// Write the reserved memory block to a buffer.
fn write_reserved_memory(&self, mut writer: impl io::Write) -> Result<()> {
for entry in &self.reserved_memory {
entry.write_blob(&mut writer)?;
}
RESVMEM_TERMINATOR.write_blob(writer)
}
/// Load a flattened device tree from a byte slice.
///
/// # Arguments
///
/// `input` - byte slice from which to load the FDT.
pub fn from_blob(input: Blob) -> Result<Self> {
let header = input
.get(..FdtHeader::SIZE)
.ok_or_else(|| Error::FdtParseError("cannot extract header, input too small".into()))?;
let header = FdtHeader::from_blob(header)?;
if header.total_size as usize > input.len() {
return Err(Error::FdtParseError("input size doesn't match".into()));
}
let reserved_mem_blob = &input[header.off_mem_rsvmap as usize..];
let nodes_blob = &input[header.off_dt_struct as usize
..(header.off_dt_struct + header.size_dt_struct) as usize];
let strings_blob = &input[header.off_dt_strings as usize
..(header.off_dt_strings + header.size_dt_strings) as usize];
let reserved_memory = Self::parse_reserved_memory(reserved_mem_blob)?;
let strings = FdtStrings::from_blob(strings_blob)?;
let root = FdtNode::from_blob(nodes_blob, &strings)?;
Ok(Self {
reserved_memory,
root,
strings,
boot_cpuid_phys: header.boot_cpuid_phys,
})
}
// Write the structure block of the FDT
fn write_struct(&mut self, mut writer: impl io::Write) -> Result<()> {
self.root.write_blob(&mut writer, &mut self.strings)?;
writer.write_all(&FDT_END.to_be_bytes())?;
Ok(())
}
/// Finish writing the Devicetree Blob (DTB).
///
/// Returns the DTB as a vector of bytes.
pub fn finish(&mut self) -> Result<Vec<u8>> {
let mut result = vec![0u8; FdtHeader::SIZE];
align_data(&mut result, SIZE_U64);
let off_mem_rsvmap = result.len();
self.write_reserved_memory(&mut result)?;
align_data(&mut result, SIZE_U64);
let off_dt_struct = result.len();
self.write_struct(&mut result)?;
align_data(&mut result, SIZE_U32);
let off_dt_strings = result.len();
self.strings.write_blob(&mut result)?;
let total_size = u32::try_from(result.len()).map_err(|_| Error::TotalSizeTooLarge)?;
let header = FdtHeader::new(
total_size,
off_dt_struct as u32,
off_dt_strings as u32,
off_mem_rsvmap as u32,
self.boot_cpuid_phys,
total_size - off_dt_strings as u32, // strings size
off_dt_strings as u32 - off_dt_struct as u32, // struct size
);
header.write_blob(&mut result[..FdtHeader::SIZE])?;
Ok(result)
}
/// Return a mutable reference to the root node of the FDT.
pub fn root_mut(&mut self) -> &mut FdtNode {
&mut self.root
}
/// Return a reference to the node the path points to, or `None` if it doesn't exist.
///
/// # Arguments
///
/// `path` - device tree path of the target node.
pub fn get_node<T: TryInto<Path>>(&self, path: T) -> Option<&FdtNode> {
let mut result_node = &self.root;
let path: Path = path.try_into().ok()?;
for node_name in path.iter() {
result_node = result_node.subnodes.get(node_name)?;
}
Some(result_node)
}
/// Return a mutable reference to the node the path points to, or `None` if it
/// doesn't exist.
///
/// # Arguments
///
/// `path` - device tree path of the target node.
pub fn get_node_mut<T: TryInto<Path>>(&mut self, path: T) -> Option<&mut FdtNode> {
let mut result_node = &mut self.root;
let path: Path = path.try_into().ok()?;
for node_name in path.iter() {
result_node = result_node.subnodes.get_mut(node_name)?;
}
Some(result_node)
}
/// Find a device tree path to the symbol exported by the FDT. The symbol must be a node label.
///
/// # Arguments
///
/// `symbol` - symbol to search for.
pub fn symbol_to_path(&self, symbol: &str) -> Result<Path> {
const SYMBOLS_NODE: &str = "__symbols__";
let Some(symbols_node) = self.root.subnode(SYMBOLS_NODE) else {
return Err(Error::InvalidPath("no symbols in fdt".into()));
};
symbols_node
.get_prop::<String>(symbol)
.ok_or_else(|| Error::InvalidName(format!("filter symbol {symbol} does not exist")))?
.parse()
}
}
#[cfg(test)]
mod tests {
use super::*;
const FDT_BLOB_HEADER_ONLY: [u8; 0x48] = [
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
];
const FDT_BLOB_RSVMAP: [u8; 0x68] = [
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
];
const FDT_BLOB_STRINGS: [u8; 0x26] = [
b'n', b'u', b'l', b'l', 0x00, b'u', b'3', b'2', 0x00, b'u', b'6', b'4', 0x00, b's', b't',
b'r', 0x00, b's', b't', b'r', b'l', b's', b't', 0x00, b'a', b'r', b'r', b'u', b'3', b'2',
0x00, b'a', b'r', b'r', b'u', b'6', b'4', 0x00,
];
const EXPECTED_STRINGS: [&str; 7] = ["null", "u32", "u64", "str", "strlst", "arru32", "arru64"];
const FDT_BLOB_NODES_ROOT_ONLY: [u8; 0x90] = [
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // node name ("") + padding
0x00, 0x00, 0x00, 0x03, // FDT_PROP (null)
0x00, 0x00, 0x00, 0x00, // prop len (0)
0x00, 0x00, 0x00, 0x00, // prop nameoff (0)
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32)
0x00, 0x00, 0x00, 0x04, // prop len (4)
0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
0x12, 0x34, 0x56, 0x78, // prop u32 value (0x12345678)
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64)
0x00, 0x00, 0x00, 0x08, // prop len (8)
0x00, 0x00, 0x00, 0x09, // prop nameoff (0x09)
0x12, 0x34, 0x56, 0x78, // prop u64 value high (0x12345678)
0x87, 0x65, 0x43, 0x21, // prop u64 value low (0x87654321)
0x00, 0x00, 0x00, 0x03, // FDT_PROP (string)
0x00, 0x00, 0x00, 0x06, // prop len (6)
0x00, 0x00, 0x00, 0x0D, // prop nameoff (0x0D)
b'h', b'e', b'l', b'l', // prop str value ("hello") + padding
b'o', 0x00, 0x00, 0x00, // "o\0" + padding
0x00, 0x00, 0x00, 0x03, // FDT_PROP (string list)
0x00, 0x00, 0x00, 0x07, // prop len (7)
0x00, 0x00, 0x00, 0x11, // prop nameoff (0x11)
b'h', b'i', 0x00, b'b', // prop value ("hi", "bye")
b'y', b'e', 0x00, 0x00, // "ye\0" + padding
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u32 array)
0x00, 0x00, 0x00, 0x08, // prop len (8)
0x00, 0x00, 0x00, 0x18, // prop nameoff (0x18)
0x12, 0x34, 0x56, 0x78, // prop value 0
0xAA, 0xBB, 0xCC, 0xDD, // prop value 1
0x00, 0x00, 0x00, 0x03, // FDT_PROP (u64 array)
0x00, 0x00, 0x00, 0x08, // prop len (8)
0x00, 0x00, 0x00, 0x1f, // prop nameoff (0x1F)
0x12, 0x34, 0x56, 0x78, // prop u64 value 0 high
0x87, 0x65, 0x43, 0x21, // prop u64 value 0 low
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // FDT_END
];
/*
Node structure:
/
|- nested
|- nested2
|- nested3
*/
const FDT_BLOB_NESTED_NODES: [u8; 0x80] = [
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // node name ("") + padding
0x00, 0x00, 0x00, 0x03, // FDT_PROP
0x00, 0x00, 0x00, 0x04, // prop len (4)
0x00, 0x00, 0x00, 0x00, // prop nameoff (0x00)
0x13, 0x57, 0x90, 0x24, // prop u32 value (0x13579024)
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
b'n', b'e', b's', b't', // Node name ("nested")
b'e', b'd', 0x00, 0x00, // "ed\0" + pad
0x00, 0x00, 0x00, 0x03, // FDT_PROP
0x00, 0x00, 0x00, 0x04, // prop len (4)
0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
0x12, 0x12, 0x12, 0x12, // prop u32 value (0x12121212)
0x00, 0x00, 0x00, 0x03, // FDT_PROP
0x00, 0x00, 0x00, 0x04, // prop len (4)
0x00, 0x00, 0x00, 0x18, // prop nameoff (0x18)
0x13, 0x57, 0x90, 0x24, // prop u32 value (0x13579024)
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested")
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
b'n', b'e', b's', b't', // Node name ("nested2")
b'e', b'd', b'2', 0x00, // "ed2\0"
0x00, 0x00, 0x00, 0x03, // FDT_PROP
0x00, 0x00, 0x00, 0x04, // prop len (0)
0x00, 0x00, 0x00, 0x05, // prop nameoff (0x05)
0x12, 0x12, 0x12, 0x12, // prop u32 value (0x12121212)
0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE
b'n', b'e', b's', b't', // Node name ("nested3")
b'e', b'd', b'3', 0x00, // "ed3\0"
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested3")
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("nested2")
0x00, 0x00, 0x00, 0x02, // FDT_END_NODE ("")
0x00, 0x00, 0x00, 0x09, // FDT_END
];
#[test]
fn fdt_load_header() {
let blob: &[u8] = &FDT_BLOB_HEADER_ONLY;
let header = FdtHeader::from_blob(blob).unwrap();
assert_eq!(header.magic, FdtHeader::MAGIC);
assert_eq!(header.total_size, 0x48);
assert_eq!(header.off_dt_struct, 0x38);
assert_eq!(header.off_dt_strings, 0x48);
assert_eq!(header.off_mem_rsvmap, 0x28);
assert_eq!(header.version, 17);
assert_eq!(header.last_comp_version, 16);
assert_eq!(header.boot_cpuid_phys, 0);
assert_eq!(header.size_dt_strings, 0);
assert_eq!(header.size_dt_struct, 0x10);
}
#[test]
fn fdt_load_invalid_header() {
// HEADER is valid
const HEADER: [u8; 40] = [
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0xda, // 0004: totalsize (0xda)
0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
0x00, 0x00, 0x00, 0xb2, // 000C: off_dt_strings (0xb2)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x28, // 0020: size_dt_strings (0x28)
0x00, 0x00, 0x00, 0x5a, // 0024: size_dt_struct (0x5a)
];
FdtHeader::from_blob(&HEADER).unwrap();
// Header too small
assert!(FdtHeader::from_blob(&HEADER[..FdtHeader::SIZE - 4]).is_err());
assert!(FdtHeader::from_blob(&[]).is_err());
let mut invalid_header = HEADER;
invalid_header[0x00] = 0x00; // change magic to (0x000dfeed)
FdtHeader::from_blob(&invalid_header).expect_err("invalid magic");
let mut invalid_header = HEADER;
invalid_header[0x07] = 0x10; // make totalsize too small
FdtHeader::from_blob(&invalid_header).expect_err("invalid totalsize");
let mut invalid_header = HEADER;
invalid_header[0x0b] = 0x60; // increase off_dt_struct
FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
let mut invalid_header = HEADER;
invalid_header[0x27] = 0x5c; // increase size_dt_struct
FdtHeader::from_blob(&invalid_header).expect_err("dt struct overlaps with strings");
let mut invalid_header = HEADER;
invalid_header[0x13] = 0x20; // decrease off_mem_rsvmap
FdtHeader::from_blob(&invalid_header).expect_err("reserved memory overlaps with header");
let mut invalid_header = HEADER;
invalid_header[0x0f] = 0x50; // decrease off_dt_strings
FdtHeader::from_blob(&invalid_header).expect_err("strings start before struct");
let mut invalid_header = HEADER;
invalid_header[0x23] = 0x50; // increase size_dt_strings
FdtHeader::from_blob(&invalid_header).expect_err("strings go past totalsize");
}
#[test]
fn fdt_load_resv_map() {
let blob: &[u8] = &FDT_BLOB_RSVMAP;
let fdt = Fdt::from_blob(blob).unwrap();
assert_eq!(fdt.reserved_memory.len(), 2);
assert!(
fdt.reserved_memory[0].address == 0x12345678AABBCCDD
&& fdt.reserved_memory[0].size == 0x1234
);
assert!(
fdt.reserved_memory[1].address == 0x1020304050607080
&& fdt.reserved_memory[1].size == 0x5678
);
}
#[test]
fn fdt_test_node_props() {
let mut node = FdtNode::empty("mynode").unwrap();
node.set_prop("myprop", 1u32).unwrap();
assert_eq!(node.get_prop::<u32>("myprop").unwrap(), 1u32);
node.set_prop("myprop", 0xabcdef9876543210u64).unwrap();
assert_eq!(
node.get_prop::<u64>("myprop").unwrap(),
0xabcdef9876543210u64
);
node.set_prop("myprop", ()).unwrap();
assert_eq!(node.get_prop::<Vec<u8>>("myprop").unwrap(), []);
node.set_prop("myprop", vec![1u8, 2u8, 3u8]).unwrap();
assert_eq!(
node.get_prop::<Vec<u8>>("myprop").unwrap(),
vec![1u8, 2u8, 3u8]
);
node.set_prop("myprop", vec![1u32, 2u32, 3u32]).unwrap();
assert_eq!(
node.get_prop::<Vec<u32>>("myprop").unwrap(),
vec![1u32, 2u32, 3u32]
);
node.set_prop("myprop", vec![1u64, 2u64, 3u64]).unwrap();
assert_eq!(
node.get_prop::<Vec<u64>>("myprop").unwrap(),
vec![1u64, 2u64, 3u64]
);
node.set_prop("myprop", "myval".to_string()).unwrap();
assert_eq!(
node.get_prop::<String>("myprop").unwrap(),
"myval".to_string()
);
node.set_prop(
"myprop",
vec![
"myval1".to_string(),
"myval2".to_string(),
"myval3".to_string(),
],
)
.unwrap();
assert_eq!(
node.get_prop::<Vec<String>>("myprop").unwrap(),
vec![
"myval1".to_string(),
"myval2".to_string(),
"myval3".to_string()
]
);
}
#[test]
fn fdt_simple_use() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node
.set_prop("compatible", "linux,dummy-virt")
.unwrap();
root_node.set_prop("#address-cells", 0x2u32).unwrap();
root_node.set_prop("#size-cells", 0x2u32).unwrap();
let chosen_node = root_node.subnode_mut("chosen").unwrap();
chosen_node.set_prop("linux,pci-probe-only", 1u32).unwrap();
chosen_node
.set_prop("bootargs", "panic=-1 console=hvc0 root=/dev/vda")
.unwrap();
fdt.finish().unwrap();
}
#[test]
fn fdt_load_strings() {
let blob = &FDT_BLOB_STRINGS[..];
let strings = FdtStrings::from_blob(blob).unwrap();
let mut offset = 0u32;
for s in EXPECTED_STRINGS {
assert_eq!(strings.at_offset(offset).unwrap(), s);
offset += strings.at_offset(offset).unwrap().len() as u32 + 1;
}
}
#[test]
fn fdt_load_strings_intern() {
let strings_blob = &FDT_BLOB_STRINGS[..];
let mut strings = FdtStrings::from_blob(strings_blob).unwrap();
assert_eq!(strings.intern_string("null"), 0);
assert_eq!(strings.intern_string("strlst"), 17);
assert_eq!(strings.intern_string("arru64"), 31);
assert_eq!(strings.intern_string("abc"), 38);
assert_eq!(strings.intern_string("def"), 42);
assert_eq!(strings.intern_string("strlst"), 17);
}
#[test]
fn fdt_load_props() {
const PROP_SIZES: [(&str, usize); 7] = [
("null", 0),
("u32", 4),
("u64", 8),
("str", 6),
("strlst", 7),
("arru32", 8),
("arru64", 8),
];
let blob: &[u8] = &FDT_BLOB_STRINGS[..];
let strings = FdtStrings::from_blob(blob).unwrap();
let blob: &[u8] = &FDT_BLOB_NODES_ROOT_ONLY[..];
let node = FdtNode::from_blob(blob, &strings).unwrap();
assert_eq!(node.name, "");
assert_eq!(node.subnodes.len(), 0);
assert_eq!(node.props.len(), PROP_SIZES.len());
for (pname, s) in PROP_SIZES.into_iter() {
assert_eq!(node.get_prop::<Vec<u8>>(pname).unwrap().len(), s);
}
}
#[test]
fn fdt_load_nodes_nested() {
let strings_blob = &FDT_BLOB_STRINGS[..];
let strings = FdtStrings::from_blob(strings_blob).unwrap();
let blob: &[u8] = &FDT_BLOB_NESTED_NODES[..];
let root_node = FdtNode::from_blob(blob, &strings).unwrap();
// Check root node
assert_eq!(root_node.name, "");
assert_eq!(root_node.subnodes.len(), 2);
assert_eq!(root_node.props.len(), 1);
// Check first nested node
let nested_node = root_node.subnodes.get("nested").unwrap();
assert_eq!(nested_node.name, "nested");
assert_eq!(nested_node.subnodes.len(), 0);
assert_eq!(nested_node.props.len(), 2);
// Check second nested node
let nested2_node = root_node.subnodes.get("nested2").unwrap();
assert_eq!(nested2_node.name, "nested2");
assert_eq!(nested2_node.subnodes.len(), 1);
assert_eq!(nested2_node.props.len(), 1);
// Check third nested node
let nested3_node = nested2_node.subnodes.get("nested3").unwrap();
assert_eq!(nested3_node.name, "nested3");
assert_eq!(nested3_node.subnodes.len(), 0);
assert_eq!(nested3_node.props.len(), 0);
}
#[test]
fn fdt_get_node() {
let fdt = Fdt::new(&[]);
assert!(fdt.get_node("/").is_some());
assert!(fdt.get_node("/a").is_none());
}
#[test]
fn fdt_find_nested_node() {
let mut fdt = Fdt::new(&[]);
let node1 = fdt.root.subnode_mut("N1").unwrap();
node1.subnode_mut("N1-1").unwrap();
node1.subnode_mut("N1-2").unwrap();
let node2 = fdt.root.subnode_mut("N2").unwrap();
let node2_1 = node2.subnode_mut("N2-1").unwrap();
node2_1.subnode_mut("N2-1-1").unwrap();
assert!(fdt.get_node("/").is_some());
assert!(fdt.get_node("/N1").is_some());
assert!(fdt.get_node("/N2").is_some());
assert!(fdt.get_node("/N1/N1-1").is_some());
assert!(fdt.get_node("/N1/N1-2").is_some());
assert!(fdt.get_node("/N2/N2-1").is_some());
assert!(fdt.get_node("/N2/N2-1/N2-1-1").is_some());
assert!(fdt.get_node("/N2/N2-1/A").is_none());
}
#[test]
fn minimal() {
let mut fdt = Fdt::new(&[]);
assert_eq!(
fdt.finish().unwrap(),
[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x48, // 0004: totalsize (0x48)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x48, // 000C: off_dt_strings (0x48)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x02, // 0040: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0044: FDT_END
]
);
}
#[test]
fn reservemap() {
let mut fdt = Fdt::new(&[
FdtReserveEntry {
address: 0x12345678AABBCCDD,
size: 0x1234,
},
FdtReserveEntry {
address: 0x1020304050607080,
size: 0x5678,
},
]);
assert_eq!(
fdt.finish().unwrap(),
[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x68, // 0004: totalsize (0x68)
0x00, 0x00, 0x00, 0x58, // 0008: off_dt_struct (0x58)
0x00, 0x00, 0x00, 0x68, // 000C: off_dt_strings (0x68)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0)
0x00, 0x00, 0x00, 0x10, // 0024: size_dt_struct (0x10)
0x12, 0x34, 0x56, 0x78, // 0028: rsvmap entry 0 address high
0xAA, 0xBB, 0xCC, 0xDD, // 002C: rsvmap entry 0 address low
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap entry 0 size high
0x00, 0x00, 0x12, 0x34, // 0034: rsvmap entry 0 size low
0x10, 0x20, 0x30, 0x40, // 0038: rsvmap entry 1 address high
0x50, 0x60, 0x70, 0x80, // 003C: rsvmap entry 1 address low
0x00, 0x00, 0x00, 0x00, // 0040: rsvmap entry 1 size high
0x00, 0x00, 0x56, 0x78, // 0044: rsvmap entry 1 size low
0x00, 0x00, 0x00, 0x00, // 0048: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 004C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0050: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0054: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 005C: node name ("") + padding
0x00, 0x00, 0x00, 0x02, // 0060: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0064: FDT_END
]
);
}
#[test]
fn prop_null() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node.set_prop("null", ()).unwrap();
assert_eq!(
fdt.finish().unwrap(),
[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x59, // 0004: totalsize (0x59)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x54, // 000C: off_dt_strings (0x54)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x05, // 0020: size_dt_strings (0x05)
0x00, 0x00, 0x00, 0x1c, // 0024: size_dt_struct (0x1C)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
0x00, 0x00, 0x00, 0x00, // 0044: prop len (0)
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
0x00, 0x00, 0x00, 0x02, // 004C: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0050: FDT_END
b'n', b'u', b'l', b'l', 0x00, // 0054: strings block
]
);
}
#[test]
fn prop_u32() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node.set_prop("u32", 0x12345678u32).unwrap();
assert_eq!(
fdt.finish().unwrap(),
[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x5c, // 0004: totalsize (0x5C)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x58, // 000C: off_dt_strings (0x58)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x04, // 0020: size_dt_strings (0x04)
0x00, 0x00, 0x00, 0x20, // 0024: size_dt_struct (0x20)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0)
0x12, 0x34, 0x56, 0x78, // 004C: prop u32 value (0x12345678)
0x00, 0x00, 0x00, 0x02, // 0050: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0054: FDT_END
b'u', b'3', b'2', 0x00, // 0058: strings block
]
);
}
#[test]
fn all_props() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node
.set_prop("arru32", &[0x12345678u32, 0xAABBCCDDu32])
.unwrap();
root_node
.set_prop("arru64", &[0x1234567887654321u64])
.unwrap();
root_node.set_prop("null", ()).unwrap();
root_node.set_prop("str", "hello").unwrap();
root_node.set_prop("strlst", &["hi", "bye"]).unwrap();
root_node.set_prop("u32", 0x12345678u32).unwrap();
root_node.set_prop("u64", 0x1234567887654321u64).unwrap();
assert_eq!(
fdt.finish().unwrap(),
[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0xee, // 0004: totalsize (0xEE)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0xc8, // 000C: off_dt_strings (0xC8)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x26, // 0020: size_dt_strings (0x26)
0x00, 0x00, 0x00, 0x90, // 0024: size_dt_struct (0x90)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (u32 array)
0x00, 0x00, 0x00, 0x08, // 0044: prop len (8)
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
0x12, 0x34, 0x56, 0x78, // 004C: prop value 0
0xAA, 0xBB, 0xCC, 0xDD, // 0050: prop value 1
0x00, 0x00, 0x00, 0x03, // 0054: FDT_PROP (u64 array)
0x00, 0x00, 0x00, 0x08, // 0058: prop len (8)
0x00, 0x00, 0x00, 0x07, // 005C: prop nameoff (0x07)
0x12, 0x34, 0x56, 0x78, // 0060: prop u64 value 0 high
0x87, 0x65, 0x43, 0x21, // 0064: prop u64 value 0 low
0x00, 0x00, 0x00, 0x03, // 0068: FDT_PROP (null)
0x00, 0x00, 0x00, 0x00, // 006C: prop len (0)
0x00, 0x00, 0x00, 0x0E, // 0070: prop nameoff (0x0e)
0x00, 0x00, 0x00, 0x03, // 0074: FDT_PROP (string)
0x00, 0x00, 0x00, 0x06, // 0078: prop len (6)
0x00, 0x00, 0x00, 0x13, // 007C: prop nameoff (0x13)
b'h', b'e', b'l', b'l', // 0080: prop str value ("hello") + padding
b'o', 0x00, 0x00, 0x00, // 0084: "o\0" + padding
0x00, 0x00, 0x00, 0x03, // 0088: FDT_PROP (string list)
0x00, 0x00, 0x00, 0x07, // 008C: prop len (7)
0x00, 0x00, 0x00, 0x17, // 0090: prop nameoff (0x17)
b'h', b'i', 0x00, b'b', // 0094: prop value ("hi", "bye")
b'y', b'e', 0x00, 0x00, // 0098: "ye\0" + padding
0x00, 0x00, 0x00, 0x03, // 009C: FDT_PROP (u32)
0x00, 0x00, 0x00, 0x04, // 00A0: prop len (4)
0x00, 0x00, 0x00, 0x1E, // 00A4: prop nameoff (0x1E)
0x12, 0x34, 0x56, 0x78, // 00A8: prop u32 value (0x12345678)
0x00, 0x00, 0x00, 0x03, // 00AC: FDT_PROP (u64)
0x00, 0x00, 0x00, 0x08, // 00B0: prop len (8)
0x00, 0x00, 0x00, 0x22, // 00B4: prop nameoff (0x22)
0x12, 0x34, 0x56, 0x78, // 00B8: prop u64 value high (0x12345678)
0x87, 0x65, 0x43, 0x21, // 00BC: prop u64 value low (0x87654321)
0x00, 0x00, 0x00, 0x02, // 00C0: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 00C4: FDT_END
b'a', b'r', b'r', b'u', b'3', b'2', 0x00, // 00C8: strings + 0x00: "arru32"
b'a', b'r', b'r', b'u', b'6', b'4', 0x00, // 00CF: strings + 0x07: "arru64"
b'n', b'u', b'l', b'l', 0x00, // 00D6: strings + 0x0E: "null"
b's', b't', b'r', 0x00, // 00DB: strings + 0x13: "str"
b's', b't', b'r', b'l', b's', b't', 0x00, // 00DF: strings + 0x17: "strlst"
b'u', b'3', b'2', 0x00, // 00E6: strings + 0x1E: "u32"
b'u', b'6', b'4', 0x00, // 00EA: strings + 0x22: "u64"
]
);
}
#[test]
fn node_order() {
let expected: &[u8] = &[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x9C, // 0004: totalsize (0x9C)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x9C, // 000C: off_dt_strings (0x9C)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x00, // 0020: size_dt_strings (0x00)
0x00, 0x00, 0x00, 0x64, // 0024: size_dt_struct (0x64)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x01, // 0040: FDT_BEGIN_NODE
b'B', 0x00, 0x00, 0x00, // 0044: node name ("B") + padding
0x00, 0x00, 0x00, 0x02, // 0048: FDT_END_NODE
0x00, 0x00, 0x00, 0x01, // 004C: FDT_BEGIN_NODE
b'A', 0x00, 0x00, 0x00, // 0050: node name ("A") + padding
0x00, 0x00, 0x00, 0x02, // 0054: FDT_END_NODE
0x00, 0x00, 0x00, 0x01, // 0058: FDT_BEGIN_NODE
b'C', 0x00, 0x00, 0x00, // 005C: node name ("C") + padding
0x00, 0x00, 0x00, 0x01, // 0060: FDT_BEGIN_NODE
b'D', 0x00, 0x00, 0x00, // 0064: node name ("D") + padding
0x00, 0x00, 0x00, 0x02, // 0068: FDT_END_NODE
0x00, 0x00, 0x00, 0x01, // 006C: FDT_BEGIN_NODE
b'E', 0x00, 0x00, 0x00, // 0070: node name ("E") + padding
0x00, 0x00, 0x00, 0x02, // 0074: FDT_END_NODE
0x00, 0x00, 0x00, 0x01, // 0078: FDT_BEGIN_NODE
b'B', 0x00, 0x00, 0x00, // 007C: node name ("B") + padding
0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE
0x00, 0x00, 0x00, 0x01, // 0084: FDT_BEGIN_NODE
b'F', 0x00, 0x00, 0x00, // 0088: node name ("F") + padding
0x00, 0x00, 0x00, 0x02, // 008C: FDT_END_NODE
0x00, 0x00, 0x00, 0x02, // 0090: FDT_END_NODE
0x00, 0x00, 0x00, 0x02, // 0094: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0098: FDT_END
];
let mut fdt = Fdt::new(&[]);
let root = fdt.root_mut();
let root_subnode_names = ["B", "A", "C"];
let node_c_subnode_names = ["D", "E", "B", "F"];
for n in root_subnode_names {
root.subnode_mut(n).unwrap();
}
let node_c = root.subnode_mut("C").unwrap();
for n in node_c_subnode_names {
node_c.subnode_mut(n).unwrap();
}
assert!(root
.iter_subnodes()
.zip(root_subnode_names)
.all(|(sn, n)| sn.name == n));
assert!(root
.subnode("C")
.unwrap()
.iter_subnodes()
.zip(node_c_subnode_names)
.all(|(sn, n)| sn.name == n));
assert_eq!(fdt.finish().unwrap(), expected);
}
#[test]
fn prop_order() {
let expected: &[u8] = &[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x98, // 0004: totalsize (0x98)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x10, // 0020: size_dt_strings (0x10)
0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP (u32)
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
0x76, 0x61, 0x6c, 0x00, // 004C: prop string value ("val")
0x00, 0x00, 0x00, 0x03, // 0050: FDT_PROP (u32)
0x00, 0x00, 0x00, 0x04, // 0054: prop len (4)
0x00, 0x00, 0x00, 0x04, // 0058: prop nameoff (0x04)
0x00, 0x00, 0x00, 0x02, // 005C: prop u32 high (0x2)
0x00, 0x00, 0x00, 0x03, // 0060: FDT_PROP (u32)
0x00, 0x00, 0x00, 0x04, // 0064: prop len (4)
0x00, 0x00, 0x00, 0x08, // 0068: prop nameoff (0x08)
0x00, 0x00, 0x00, 0x01, // 006C: prop u32 value (0x1)
0x00, 0x00, 0x00, 0x03, // 0070: FDT_PROP (u32)
0x00, 0x00, 0x00, 0x04, // 0074: prop len (4)
0x00, 0x00, 0x00, 0x0C, // 0078: prop nameoff (0x0B)
0x00, 0x00, 0x00, 0x03, // 007C: prop u32 value (0x3)
0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE
0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
b'g', b'h', b'i', 0x00, // 0088: strings + 0x00: "ghi"
b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
b'a', b'b', b'c', 0x00, // 0090: strings + 0x08: "abc"
b'b', b'c', b'd', 0x00, // 0094: strings + 0x0C: "bcd"
];
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node.set_prop("ghi", "val").unwrap();
root_node.set_prop("def", 2u32).unwrap();
root_node.set_prop("abc", 1u32).unwrap();
root_node.set_prop("bcd", 3u32).unwrap();
assert_eq!(
root_node.prop_names().collect::<Vec<_>>(),
["ghi", "def", "abc", "bcd"]
);
assert_eq!(fdt.finish().unwrap(), expected);
}
#[test]
fn nested_nodes() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node.set_prop("abc", 0x13579024u32).unwrap();
let nested_node = root_node.subnode_mut("nested").unwrap();
nested_node.set_prop("def", 0x12121212u32).unwrap();
assert_eq!(
fdt.finish().unwrap(),
[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x80, // 0004: totalsize (0x80)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x78, // 000C: off_dt_strings (0x78)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
0x00, 0x00, 0x00, 0x40, // 0024: size_dt_struct (0x40)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
b'n', b'e', b's', b't', // 0054: Node name ("nested")
b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
0x00, 0x00, 0x00, 0x04, // 0064: prop nameoff (0x04)
0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
0x00, 0x00, 0x00, 0x02, // 006C: FDT_END_NODE ("nested")
0x00, 0x00, 0x00, 0x02, // 0070: FDT_END_NODE ("")
0x00, 0x00, 0x00, 0x09, // 0074: FDT_END
b'a', b'b', b'c', 0x00, // 0078: strings + 0x00: "abc"
b'd', b'e', b'f', 0x00, // 007C: strings + 0x04: "def"
]
);
}
#[test]
fn prop_name_string_reuse() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node.set_prop("abc", 0x13579024u32).unwrap();
let nested = root_node.subnode_mut("nested").unwrap();
nested.set_prop("abc", 0x12121212u32).unwrap(); // This should reuse the "abc" string.
nested.set_prop("def", 0x12121212u32).unwrap();
assert_eq!(
fdt.finish().unwrap(),
[
0xd0, 0x0d, 0xfe, 0xed, // 0000: magic (0xd00dfeed)
0x00, 0x00, 0x00, 0x90, // 0004: totalsize (0x90)
0x00, 0x00, 0x00, 0x38, // 0008: off_dt_struct (0x38)
0x00, 0x00, 0x00, 0x88, // 000C: off_dt_strings (0x88)
0x00, 0x00, 0x00, 0x28, // 0010: off_mem_rsvmap (0x28)
0x00, 0x00, 0x00, 0x11, // 0014: version (0x11 = 17)
0x00, 0x00, 0x00, 0x10, // 0018: last_comp_version (0x10 = 16)
0x00, 0x00, 0x00, 0x00, // 001C: boot_cpuid_phys (0)
0x00, 0x00, 0x00, 0x08, // 0020: size_dt_strings (0x08)
0x00, 0x00, 0x00, 0x50, // 0024: size_dt_struct (0x50)
0x00, 0x00, 0x00, 0x00, // 0028: rsvmap terminator (address = 0 high)
0x00, 0x00, 0x00, 0x00, // 002C: rsvmap terminator (address = 0 low)
0x00, 0x00, 0x00, 0x00, // 0030: rsvmap terminator (size = 0 high)
0x00, 0x00, 0x00, 0x00, // 0034: rsvmap terminator (size = 0 low)
0x00, 0x00, 0x00, 0x01, // 0038: FDT_BEGIN_NODE
0x00, 0x00, 0x00, 0x00, // 003C: node name ("") + padding
0x00, 0x00, 0x00, 0x03, // 0040: FDT_PROP
0x00, 0x00, 0x00, 0x04, // 0044: prop len (4)
0x00, 0x00, 0x00, 0x00, // 0048: prop nameoff (0x00)
0x13, 0x57, 0x90, 0x24, // 004C: prop u32 value (0x13579024)
0x00, 0x00, 0x00, 0x01, // 0050: FDT_BEGIN_NODE
b'n', b'e', b's', b't', // 0054: Node name ("nested")
b'e', b'd', 0x00, 0x00, // 0058: "ed\0" + pad
0x00, 0x00, 0x00, 0x03, // 005C: FDT_PROP
0x00, 0x00, 0x00, 0x04, // 0060: prop len (4)
0x00, 0x00, 0x00, 0x00, // 0064: prop nameoff (0x00 - reuse)
0x12, 0x12, 0x12, 0x12, // 0068: prop u32 value (0x12121212)
0x00, 0x00, 0x00, 0x03, // 006C: FDT_PROP
0x00, 0x00, 0x00, 0x04, // 0070: prop len (4)
0x00, 0x00, 0x00, 0x04, // 0074: prop nameoff (0x04)
0x12, 0x12, 0x12, 0x12, // 0078: prop u32 value (0x12121212)
0x00, 0x00, 0x00, 0x02, // 007C: FDT_END_NODE ("nested")
0x00, 0x00, 0x00, 0x02, // 0080: FDT_END_NODE ("")
0x00, 0x00, 0x00, 0x09, // 0084: FDT_END
b'a', b'b', b'c', 0x00, // 0088: strings + 0x00: "abc"
b'd', b'e', b'f', 0x00, // 008C: strings + 0x04: "def"
]
);
}
#[test]
fn invalid_node_name_nul() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node
.subnode_mut("abc\0def")
.expect_err("node name with embedded NUL");
}
#[test]
fn invalid_prop_name_nul() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node
.set_prop("abc\0def", 0u32)
.expect_err("property name with embedded NUL");
}
#[test]
fn invalid_prop_string_value_nul() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
root_node
.set_prop("mystr", "abc\0def")
.expect_err("string property value with embedded NUL");
}
#[test]
fn invalid_prop_string_list_value_nul() {
let mut fdt = Fdt::new(&[]);
let root_node = fdt.root_mut();
let strs = ["test", "abc\0def"];
root_node
.set_prop("mystr", &strs)
.expect_err("stringlist property value with embedded NUL");
}
}