-
Notifications
You must be signed in to change notification settings - Fork 15
/
dtb.hh
373 lines (360 loc) · 10.9 KB
/
dtb.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2013 David Chisnall
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
* ("CTSRD"), as part of the DARPA CRASH research programme.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _DTB_HH_
#define _DTB_HH_
#include <map>
#include <string>
#include <assert.h>
#include "input_buffer.hh"
#include "util.hh"
namespace dtc
{
/**
* The dtb namespace contains code related to the generation of device tree
* blobs, the binary representation of flattened device trees. The abstract
* tree representation calls into this code to generate the output.
*/
namespace dtb
{
/** The token types in the DTB, as defined by §7.4.1 of the ePAPR
* specification. All of these values are written in big-endian format in the
* output.
*/
enum token_type
{
/**
* Marker indicating the start of a node in the tree. This is followed
* by the nul-terminated name. If a unit address is specified, then
* the name also contains the address, with an @ symbol between the end
* of the name and the start of the address.
*
* The name is then padded such that the next token begins on a 4-byte
* boundary. The node may contain properties, other nodes, both, or be
* empty.
*/
FDT_BEGIN_NODE = 0x00000001,
/**
* Marker indicating the end of a node.
*/
FDT_END_NODE = 0x00000002,
/**
* The start of a property. This is followed by two 32-bit big-endian
* values. The first indicates the length of the property value, the
* second its index in the strings table. It is then followed by the
* property value, if the value is of non-zero length.
*/
FDT_PROP = 0x00000003,
/**
* Ignored token. May be used for padding inside DTB nodes.
*/
FDT_NOP = 0x00000004,
/**
* Marker indicating the end of the tree.
*/
FDT_END = 0x00000009
};
/**
* Returns the token as a string. This is used for debugging and for printing
* human-friendly error messages about malformed DTB input.
*/
inline const char *token_type_name(token_type t)
{
switch(t)
{
case FDT_BEGIN_NODE:
return "FDT_BEGIN_NODE";
case FDT_END_NODE:
return "FDT_END_NODE";
case FDT_PROP:
return "FDT_PROP";
case FDT_NOP:
return "FDT_NOP";
case FDT_END:
return "FDT_END";
}
assert(0);
// Not reached.
return nullptr;
}
/**
* Abstract class for writing a section of the output. We create one
* of these for each section that needs to be written. It is intended to build
* a temporary buffer of the output in memory and then write it to a file
* stream. The size can be returned after all of the data has been written
* into the internal buffer, so the sizes of the three tables can be calculated
* before storing them in the buffer.
*/
struct output_writer
{
/**
* Writes a label into the output stream. This is only applicable for
* assembly output, where the labels become symbols that can be
* resolved at link time.
*/
virtual void write_label(const std::string &name) = 0;
/**
* Writes a comment into the output stream. Useful only when debugging
* the output.
*/
virtual void write_comment(const std::string &name) = 0;
/**
* Writes a string. A nul terminator is implicitly added.
*/
virtual void write_string(const std::string &name) = 0;
/**
* Writes a single 8-bit value.
*/
virtual void write_data(uint8_t) = 0;
/**
* Writes a single 32-bit value. The value is written in big-endian
* format, but should be passed in the host's native endian.
*/
virtual void write_data(uint32_t) = 0;
/**
* Writes a single 64-bit value. The value is written in big-endian
* format, but should be passed in the host's native endian.
*/
virtual void write_data(uint64_t) = 0;
/**
* Writes the collected output to the specified file descriptor.
*/
virtual void write_to_file(int fd) = 0;
/**
* Returns the number of bytes.
*/
virtual uint32_t size() = 0;
/**
* Helper for writing tokens to the output stream. This writes a
* comment above the token describing its value, for easier debugging
* of the output.
*/
inline void write_token(token_type t)
{
write_comment(token_type_name(t));
write_data((uint32_t)t);
}
/**
* Helper function that writes a byte buffer to the output, one byte at
* a time.
*/
void write_data(byte_buffer b);
};
/**
* Binary file writer. This class is responsible for writing the DTB output
* directly in blob format.
*/
class binary_writer : public output_writer
{
/**
* The internal buffer used to store the blob while it is being
* constructed.
*/
byte_buffer buffer;
public:
/**
* The binary format does not support labels, so this method
* does nothing.
*/
void write_label(const std::string &) override {}
/**
* Comments are ignored by the binary writer.
*/
void write_comment(const std::string&) override {}
void write_string(const std::string &name) override;
void write_data(uint8_t v) override;
void write_data(uint32_t v) override;
void write_data(uint64_t v) override;
void write_to_file(int fd) override;
uint32_t size() override;
};
/**
* Assembly writer. This class is responsible for writing the output in an
* assembly format that is suitable for linking into a kernel, loader, and so
* on.
*/
class asm_writer : public output_writer
{
/**
* The internal buffer for temporary values. Note that this actually
* contains ASCII text, but it is a byte buffer so that we can just
* copy strings across as-is.
*/
byte_buffer buffer;
/**
* The number of bytes written to the current line. This is used to
* allow line wrapping, where we aim to write four .byte directives to
* make the alignment clearer.
*/
int byte_count;
/**
* The current number of bytes written. This is the number in binary
* format, not the number of bytes in the buffer.
*/
uint32_t bytes_written;
/**
* Writes a string directly to the output as-is. This is the function that
* performs the real output.
*/
void write_string(const char *c);
/**
* Write a string to the output.
*/
void write_string(const std::string &c) override;
/**
* Writes the string, starting on a new line.
*/
void write_line(const char *c);
/**
* Writes a byte in binary format. This will emit a single .byte
* directive, with up to four per line.
*/
void write_byte(uint8_t b);
public:
asm_writer() : byte_count(0), bytes_written(0) {}
void write_label(const std::string &name) override;
void write_comment(const std::string &name) override;
void write_data(uint8_t v) override;
void write_data(uint32_t v) override;
void write_data(uint64_t v) override;
void write_to_file(int fd) override;
uint32_t size() override;
};
/**
* Class encapsulating the device tree blob header. This class stores all of
* the values found in the header and is responsible for writing them to the
* output.
*/
struct header
{
/**
* Magic value, used to validate that this really is a device tree
* blob. Should always be set to 0xd00dfeed.
*/
uint32_t magic;
/**
* The total size of the blob, including header, reservations, strings
* table, and padding.
*/
uint32_t totalsize;
/**
* The offset from the start of the blob of the struct table (i.e. the
* part of the blob containing the entire device tree).
*/
uint32_t off_dt_struct;
/**
* The offset from the start of the blob of the strings table.
*/
uint32_t off_dt_strings;
/**
* The offset of the reservation map from the start of the blob.
*/
uint32_t off_mem_rsvmap;
/**
* The version of the blob. This should always be 17.
*/
uint32_t version;
/**
* The earliest version of the DTB specification with which this blob
* is backwards compatible. This should always be 16.
*/
uint32_t last_comp_version;
/**
* The ID of the CPU where this boots.
*/
uint32_t boot_cpuid_phys;
/**
* The size of the strings table.
*/
uint32_t size_dt_strings;
/**
* The size of the struct table.
*/
uint32_t size_dt_struct;
/**
* Writes the entire header to the specified output buffer.
*/
void write(output_writer &out);
/**
* Reads the header from bits binary representation in a blob.
*/
bool read_dtb(input_buffer &input);
/**
* Default constructor. Initialises the values that have sensible
* defaults, leaves the others blank.
*/
header() : magic(0xd00dfeed), version(17), last_comp_version(16),
boot_cpuid_phys(0) {}
};
/**
* Class encapsulating the string table. FDT strings are stored in a string
* section. This maintains a map from strings to their offsets in the strings
* section.
*
* Note: We don't currently do suffix matching, which may save a small amount
* of space.
*/
class string_table {
/**
* Map from strings to their offset.
*/
std::map<std::string, uint32_t> string_offsets;
/**
* The strings, in the order in which they should be written to the
* output. The order must be stable - adding another string must not
* change the offset of any that we have already referenced - and so we
* simply write the strings in the order that they are passed.
*/
std::vector<std::string> strings;
/**
* The current size of the strings section.
*/
uint32_t size;
public:
/**
* Default constructor, creates an empty strings table.
*/
string_table() : size(0) {}
/**
* Adds a string to the table, returning the offset from the start
* where it will be written. If the string is already present, this
* will return its existing offset, otherwise it will return a new
* offset.
*/
uint32_t add_string(const std::string &str);
/**
* Writes the strings table to the specified output.
*/
void write(dtb::output_writer &writer);
};
} // namespace dtb
} // namespace dtc
#endif // !_DTB_HH_