Alioth Code Coverage

dtb.rs54.05%

1// Copyright 2024 Google LLC
2//
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 at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// 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 and
13// limitations under the License.
14
15use std::collections::HashMap;
16use std::mem::size_of;
17
18use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};
19
20use crate::align_up;
21use crate::firmware::dt::{DeviceTree, Node, PropVal};
22use crate::utils::endian::{Bu32, Bu64};
23
24pub const FDT_HEADER_MAGIC: [u8; 4] = [0xd0, 0x0d, 0xfe, 0xed];
25pub const FDT_HEADER_VERSION: u32 = 0x11;
26pub const FDT_HEADER_LAST_COMP_VERSION: u32 = 0x10;
27
28pub const FDT_BEGIN_NODE: [u8; 4] = [0x00, 0x00, 0x00, 0x01];
29pub const FDT_END_NODE: [u8; 4] = [0x00, 0x00, 0x00, 0x02];
30pub const FDT_PROP: [u8; 4] = [0x00, 0x00, 0x00, 0x03];
31pub const FDT_NOP: [u8; 4] = [0x00, 0x00, 0x00, 0x04];
32pub const FDT_END: [u8; 4] = [0x00, 0x00, 0x00, 0x09];
33
34fn push_string_align(data: &mut Vec<u8>, s: &str) {6x
35 data.extend(s.as_bytes());6x
36 let padding = align_up!(s.len() + 1, 2) - s.len();6x
37 for _ in 0..padding {15x
38 data.push(b'\0');15x
39 }15x
40}6x
41
42fn pad_data(data: &mut Vec<u8>) {9x
43 let padding = align_up!(data.len(), 2) - data.len();9x
44 for _ in 0..padding {15x
45 data.push(b'\0');15x
46 }15x
47}9x
48
49impl PropVal {
50 fn write_as_blob(&self, data: &mut Vec<u8>) {30x
51 match self {30x
52 PropVal::Empty => {}3x
53 PropVal::U32(n) | PropVal::PHandle(n) => data.extend(n.to_be_bytes()),6x
54 PropVal::U64(n) => data.extend(n.to_be_bytes()),3x
55 PropVal::String(s) => push_string_align(data, s),3x
56 PropVal::Str(s) => push_string_align(data, s),3x
57 PropVal::Bytes(d) => {3x
58 data.extend(d);3x
59 pad_data(data);3x
60 }3x
61 PropVal::StringList(list) => {3x
62 for s in list {6x
63 data.extend(s.as_bytes());6x
64 data.push(b'\0');6x
65 }6x
66 pad_data(data)3x
67 }
68 PropVal::U32List(reg) => {3x
69 for v in reg {9x
70 data.extend(v.to_be_bytes())9x
71 }
72 }
73 PropVal::U64List(reg) => {3x
74 for v in reg {6x
75 data.extend(v.to_be_bytes())6x
76 }
77 }
78 }
79 }30x
80}
81
82#[repr(C)]
83#[derive(IntoBytes, FromBytes, Immutable, Debug)]
84pub 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)]
99pub struct FdtProp {
100 len: Bu32,
101 name_off: Bu32,
102}
103
104#[derive(IntoBytes, FromBytes, Immutable, Debug)]
105#[repr(C)]
106struct FdtReserveEntry {
107 address: Bu64,
108 size: Bu64,
109}
110
111#[derive(Debug)]
112pub struct StringBlock {
113 total_size: usize,
114 strings: HashMap<&'static str, usize>,
115}
116
117impl StringBlock {
118 fn new() -> Self {3x
119 StringBlock {3x
120 total_size: 0,3x
121 strings: HashMap::new(),3x
122 }3x
123 }3x
124
125 fn add(&mut self, name: &'static str) -> usize {12x
126 if let Some(offset) = self.strings.get(&name) {12x
127 *offset3x
128 } else {
129 self.strings.insert(name, self.total_size);9x
130 let ret = self.total_size;9x
131 self.total_size += name.len() + 1;9x
132 ret9x
133 }
134 }12x
135
136 fn write_as_blob(self, data: &mut Vec<u8>) {3x
137 let mut string_offset = self.strings.into_iter().collect::<Vec<_>>();3x
138 string_offset.sort_by_key(|(_, offset)| *offset);3x
139 for (s, _) in string_offset {9x
140 data.extend(s.as_bytes());9x
141 data.push(b'\0');9x
142 }9x
143 pad_data(data)3x
144 }3x
145}
146
147impl 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
167impl 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 data
207 }
208}
209
210#[cfg(test)]
211#[path = "dtb_test.rs"]
212mod tests;
213