dtb.rs54.05%
1
// Copyright 2024 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use std::collections::HashMap;16
use std::mem::size_of;17
18
use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};19
20
use crate::align_up;21
use crate::firmware::dt::{DeviceTree, Node, PropVal};22
use crate::utils::endian::{Bu32, Bu64};23
24
pub const FDT_HEADER_MAGIC: [u8; 4] = [0xd0, 0x0d, 0xfe, 0xed];25
pub const FDT_HEADER_VERSION: u32 = 0x11;26
pub const FDT_HEADER_LAST_COMP_VERSION: u32 = 0x10;27
28
pub const FDT_BEGIN_NODE: [u8; 4] = [0x00, 0x00, 0x00, 0x01];29
pub const FDT_END_NODE: [u8; 4] = [0x00, 0x00, 0x00, 0x02];30
pub const FDT_PROP: [u8; 4] = [0x00, 0x00, 0x00, 0x03];31
pub const FDT_NOP: [u8; 4] = [0x00, 0x00, 0x00, 0x04];32
pub const FDT_END: [u8; 4] = [0x00, 0x00, 0x00, 0x09];33
34
fn push_string_align(data: &mut Vec<u8>, s: &str) {6x35
data.extend(s.as_bytes());6x36
let padding = align_up!(s.len() + 1, 2) - s.len();6x37
for _ in 0..padding {15x38
data.push(b'\0');15x39
}15x40
}6x41
42
fn pad_data(data: &mut Vec<u8>) {9x43
let padding = align_up!(data.len(), 2) - data.len();9x44
for _ in 0..padding {15x45
data.push(b'\0');15x46
}15x47
}9x48
49
impl PropVal {50
fn write_as_blob(&self, data: &mut Vec<u8>) {30x51
match self {30x52
PropVal::Empty => {}3x53
PropVal::U32(n) | PropVal::PHandle(n) => data.extend(n.to_be_bytes()),6x54
PropVal::U64(n) => data.extend(n.to_be_bytes()),3x55
PropVal::String(s) => push_string_align(data, s),3x56
PropVal::Str(s) => push_string_align(data, s),3x57
PropVal::Bytes(d) => {3x58
data.extend(d);3x59
pad_data(data);3x60
}3x61
PropVal::StringList(list) => {3x62
for s in list {6x63
data.extend(s.as_bytes());6x64
data.push(b'\0');6x65
}6x66
pad_data(data)3x67
}68
PropVal::U32List(reg) => {3x69
for v in reg {9x70
data.extend(v.to_be_bytes())9x71
}72
}73
PropVal::U64List(reg) => {3x74
for v in reg {6x75
data.extend(v.to_be_bytes())6x76
}77
}78
}79
}30x80
}81
82
#[repr(C)]83
#[derive(IntoBytes, FromBytes, Immutable, Debug)]84
pub struct FdtHeader {85
magic: Bu32,86
total_size: Bu32,87
off_dt_struct: Bu32,88
off_dt_strings: Bu32,89
off_mem_resvmap: Bu32,90
version: Bu32,91
last_comp_version: Bu32,92
boot_cpuid_phys: Bu32,93
size_dt_strings: Bu32,94
size_dt_struct: Bu32,95
}96
97
#[derive(IntoBytes, FromBytes, Immutable, Debug)]98
#[repr(C)]99
pub struct FdtProp {100
len: Bu32,101
name_off: Bu32,102
}103
104
#[derive(IntoBytes, FromBytes, Immutable, Debug)]105
#[repr(C)]106
struct FdtReserveEntry {107
address: Bu64,108
size: Bu64,109
}110
111
#[derive(Debug)]112
pub struct StringBlock {113
total_size: usize,114
strings: HashMap<&'static str, usize>,115
}116
117
impl StringBlock {118
fn new() -> Self {3x119
StringBlock {3x120
total_size: 0,3x121
strings: HashMap::new(),3x122
}3x123
}3x124
125
fn add(&mut self, name: &'static str) -> usize {12x126
if let Some(offset) = self.strings.get(&name) {12x127
*offset3x128
} else {129
self.strings.insert(name, self.total_size);9x130
let ret = self.total_size;9x131
self.total_size += name.len() + 1;9x132
ret9x133
}134
}12x135
136
fn write_as_blob(self, data: &mut Vec<u8>) {3x137
let mut string_offset = self.strings.into_iter().collect::<Vec<_>>();3x138
string_offset.sort_by_key(|(_, offset)| *offset);3x139
for (s, _) in string_offset {9x140
data.extend(s.as_bytes());9x141
data.push(b'\0');9x142
}9x143
pad_data(data)3x144
}3x145
}146
147
impl Node {148
fn write_as_blob(&self, name: &str, string_block: &mut StringBlock, data: &mut Vec<u8>) {149
data.extend(&FDT_BEGIN_NODE);150
push_string_align(data, name);151
for (prop_name, prop) in self.props.iter() {152
data.extend(&FDT_PROP);153
let fdt_prop = FdtProp {154
len: Bu32::from(prop.size() as u32),155
name_off: Bu32::from(string_block.add(prop_name) as u32),156
};157
data.extend(fdt_prop.as_bytes());158
prop.write_as_blob(data);159
}160
for (node_name, node) in self.nodes.iter() {161
node.write_as_blob(node_name, string_block, data)162
}163
data.extend(&FDT_END_NODE);164
}165
}166
167
impl DeviceTree {168
pub fn to_blob(&self) -> Vec<u8> {169
let mut data = vec![0u8; size_of::<FdtHeader>()];170
171
let off_mem_resvmap = data.len();172
for (addr, size) in &self.reserved_mem {173
let entry = FdtReserveEntry {174
address: Bu64::from(*addr as u64),175
size: Bu64::from(*size as u64),176
};177
data.extend(entry.as_bytes());178
}179
data.extend(FdtReserveEntry::new_zeroed().as_bytes());180
181
let off_dt_struct = data.len();182
let mut string_block = StringBlock::new();183
self.root.write_as_blob("", &mut string_block, &mut data);184
data.extend(&FDT_END);185
let size_dt_struct = data.len() - off_dt_struct;186
187
let off_dt_strings = data.len();188
string_block.write_as_blob(&mut data);189
let size_dt_strings = data.len() - off_dt_strings;190
191
let total_size = data.len();192
193
let header = FdtHeader {194
magic: Bu32::from(FDT_HEADER_MAGIC),195
total_size: Bu32::from(total_size as u32),196
off_dt_struct: Bu32::from(off_dt_struct as u32),197
off_dt_strings: Bu32::from(off_dt_strings as u32),198
off_mem_resvmap: Bu32::from(off_mem_resvmap as u32),199
version: Bu32::from(FDT_HEADER_VERSION),200
last_comp_version: Bu32::from(FDT_HEADER_LAST_COMP_VERSION),201
boot_cpuid_phys: Bu32::from(self.boot_cpuid_phys),202
size_dt_strings: Bu32::from(size_dt_strings as u32),203
size_dt_struct: Bu32::from(size_dt_struct as u32),204
};205
header.write_to_prefix(&mut data).unwrap();206
data207
}208
}209
210
#[cfg(test)]211
#[path = "dtb_test.rs"]212
mod tests;213