blob: fbcf641bd5d553f31b5e3be54be351f9467e162c [file] [log] [blame]
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +01001#include "fdt.h"
2
3#include <stdint.h>
4
5#include "dlog.h"
6#include "std.h"
7
8struct fdt_header {
9 uint32_t magic;
10 uint32_t totalsize;
11 uint32_t off_dt_struct;
12 uint32_t off_dt_strings;
13 uint32_t off_mem_rsvmap;
14 uint32_t version;
15 uint32_t last_comp_version;
16 uint32_t boot_cpuid_phys;
17 uint32_t size_dt_strings;
18 uint32_t size_dt_struct;
19};
20
21struct fdt_reserve_entry {
22 uint64_t address;
23 uint64_t size;
24};
25
26enum fdt_token {
27 FDT_BEGIN_NODE = 1,
28 FDT_END_NODE = 2,
29 FDT_PROP = 3,
30 FDT_NOP = 4,
31 FDT_END = 9,
32};
33
34struct fdt_tokenizer {
35 const char *cur;
36 const char *end;
37 const char *strs;
38};
39
40#define FDT_VERSION 17
41#define FDT_MAGIC 0xd00dfeed
42
43static void fdt_tokenizer_init(struct fdt_tokenizer *t, const char *strs,
44 const char *begin, const char *end)
45{
46 t->strs = strs;
47 t->cur = begin;
48 t->end = end;
49}
50
51static void fdt_tokenizer_align(struct fdt_tokenizer *t)
52{
53 t->cur = (char *)(((size_t)t->cur + 3) & ~3);
54}
55
56static bool fdt_tokenizer_uint32(struct fdt_tokenizer *t, uint32_t *res)
57{
58 const char *next = t->cur + sizeof(*res);
Andrew Scull7364a8e2018-07-19 15:39:29 +010059 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010060 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +010061 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010062
Andrew Scull29d40392018-07-16 11:46:59 +010063 *res = be32toh(*(uint32_t *)t->cur);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010064 t->cur = next;
65
66 return true;
67}
68
69static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
70{
71 uint32_t v;
72
73 while (fdt_tokenizer_uint32(t, &v)) {
74 if (v != FDT_NOP) {
75 *res = v;
76 return true;
77 }
78 }
79 return false;
80}
81
Andrew Scull4f170f52018-07-19 12:58:20 +010082static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
83 size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010084{
85 const char *next = t->cur + size;
Andrew Scull7364a8e2018-07-19 15:39:29 +010086 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010087 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +010088 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010089
90 *res = t->cur;
91 t->cur = next;
92 fdt_tokenizer_align(t);
93
94 return true;
95}
96
97static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
98{
99 const char *p;
100 for (p = t->cur; p < t->end; p++) {
101 if (!*p) {
102 /* Found the end of the string. */
103 *res = t->cur;
104 t->cur = p + 1;
105 fdt_tokenizer_align(t);
106 return true;
107 }
108 }
109
110 return false;
111}
112
113void fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
114{
115 uint32_t max_ver;
116 uint32_t min_ver;
Andrew Scull29d40392018-07-16 11:46:59 +0100117 uint32_t begin = be32toh(hdr->off_dt_struct);
118 uint32_t size = be32toh(hdr->size_dt_struct);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100119
120 memset(node, 0, sizeof(*node));
121
122 /* Check the magic number before anything else. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100123 if (hdr->magic != be32toh(FDT_MAGIC)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100124 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100125 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100126
127 /* Check the version. */
Andrew Scull29d40392018-07-16 11:46:59 +0100128 max_ver = be32toh(hdr->version);
129 min_ver = be32toh(hdr->last_comp_version);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100130 if (FDT_VERSION < min_ver || FDT_VERSION > max_ver) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100131 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100132 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100133
134 /* TODO: Verify that it is all within the fdt. */
135 node->begin = (const char *)hdr + begin;
136 node->end = node->begin + size;
137
138 /* TODO: Verify strings as well. */
Andrew Scull29d40392018-07-16 11:46:59 +0100139 node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100140}
141
142static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
143 const char **buf, uint32_t *size)
144{
145 uint32_t token;
146 uint32_t nameoff;
147
Andrew Scull7364a8e2018-07-19 15:39:29 +0100148 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100149 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100150 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100151
152 if (token != FDT_PROP) {
153 /* Rewind so that caller will get the same token. */
154 t->cur -= sizeof(uint32_t);
155 return false;
156 }
157
158 if (!fdt_tokenizer_uint32(t, size) ||
159 !fdt_tokenizer_uint32(t, &nameoff) ||
160 !fdt_tokenizer_bytes(t, buf, *size)) {
161 /*
162 * Move cursor to the end so that caller won't get any new
163 * tokens.
164 */
165 t->cur = t->end;
166 return false;
167 }
168
169 /* TODO: Need to verify the strings. */
170 *name = t->strs + nameoff;
171
172 return true;
173}
174
175static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
176{
177 uint32_t token;
178
Andrew Scull7364a8e2018-07-19 15:39:29 +0100179 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100180 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100181 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100182
183 if (token != FDT_BEGIN_NODE) {
184 /* Rewind so that caller will get the same token. */
185 t->cur -= sizeof(uint32_t);
186 return false;
187 }
188
189 if (!fdt_tokenizer_str(t, name)) {
190 /*
191 * Move cursor to the end so that caller won't get any new
192 * tokens.
193 */
194 t->cur = t->end;
195 return false;
196 }
197
198 return true;
199}
200
201static void fdt_skip_properties(struct fdt_tokenizer *t)
202{
203 const char *name;
204 const char *buf;
205 uint32_t size;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100206 while (fdt_next_property(t, &name, &buf, &size)) {
207 /* do nothing */
208 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100209}
210
211static bool fdt_skip_node(struct fdt_tokenizer *t)
212{
213 const char *name;
214 uint32_t token;
215 size_t pending = 1;
216
217 fdt_skip_properties(t);
218
219 do {
220 while (fdt_next_subnode(t, &name)) {
221 fdt_skip_properties(t);
222 pending++;
223 }
224
Andrew Scull7364a8e2018-07-19 15:39:29 +0100225 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100226 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100227 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100228
229 if (token != FDT_END_NODE) {
230 t->cur = t->end;
231 return false;
232 }
233
234 pending--;
235 } while (pending);
236
237 return true;
238}
239
240bool fdt_read_property(const struct fdt_node *node, const char *name,
241 const char **buf, uint32_t *size)
242{
243 struct fdt_tokenizer t;
244 const char *prop_name;
245
246 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
247
248 while (fdt_next_property(&t, &prop_name, buf, size)) {
Andrew Scull7364a8e2018-07-19 15:39:29 +0100249 if (!strcmp(prop_name, name)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100250 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100251 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100252 }
253
254 return false;
255}
256
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100257bool fdt_first_child(struct fdt_node *node, const char **child_name)
258{
259 struct fdt_tokenizer t;
260
261 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
262
263 fdt_skip_properties(&t);
264
Andrew Scull7364a8e2018-07-19 15:39:29 +0100265 if (!fdt_next_subnode(&t, child_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100266 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100267 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100268
269 node->begin = t.cur;
270
271 return true;
272}
273
274bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
275{
276 struct fdt_tokenizer t;
277
278 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
279
Andrew Scull7364a8e2018-07-19 15:39:29 +0100280 if (!fdt_skip_node(&t)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100281 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100282 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100283
Andrew Scull7364a8e2018-07-19 15:39:29 +0100284 if (!fdt_next_subnode(&t, sibling_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100285 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100286 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100287
288 node->begin = t.cur;
289
290 return true;
291}
292
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100293bool fdt_find_child(struct fdt_node *node, const char *child)
294{
295 struct fdt_tokenizer t;
296 const char *name;
297
298 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
299
300 fdt_skip_properties(&t);
301
302 while (fdt_next_subnode(&t, &name)) {
303 if (!strcmp(name, child)) {
304 node->begin = t.cur;
305 return true;
306 }
307
308 fdt_skip_node(&t);
309 }
310
311 return false;
312}
313
314void fdt_dump(struct fdt_header *hdr)
315{
316 uint32_t token;
317 size_t depth = 0;
318 const char *name;
319 struct fdt_tokenizer t;
320 struct fdt_node node;
321
322 /* Traverse the whole thing. */
323 fdt_root_node(&node, hdr);
324
325 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
326
327 do {
328 while (fdt_next_subnode(&t, &name)) {
329 const char *buf;
330 uint32_t size;
331
332 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
333 depth++;
334 while (fdt_next_property(&t, &name, &buf, &size)) {
335 size_t i;
Andrew Scull4f170f52018-07-19 12:58:20 +0100336 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
337 name);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100338 for (i = 0; i < size; i++) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100339 dlog("%s%02x", i == 0 ? "" : " ",
340 buf[i]);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100341 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100342 dlog(")\n");
343 }
344 }
345
Andrew Scull7364a8e2018-07-19 15:39:29 +0100346 if (!fdt_tokenizer_token(&t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100347 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100348 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100349
Andrew Scull7364a8e2018-07-19 15:39:29 +0100350 if (token != FDT_END_NODE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100351 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100352 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100353
354 depth--;
355 } while (depth);
356
Andrew Scull29d40392018-07-16 11:46:59 +0100357 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100358 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100359 struct fdt_reserve_entry *e =
360 (struct fdt_reserve_entry
361 *)((size_t)hdr + be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100362 while (e->address || e->size) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100363 dlog("Entry: %p (0x%x bytes)\n", be64toh(e->address),
364 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100365 e++;
366 }
367 }
368}
369
370void fdt_add_mem_reservation(struct fdt_header *hdr, size_t addr, size_t len)
371{
372 /* TODO: Clean this up. */
Andrew Scull29d40392018-07-16 11:46:59 +0100373 char *begin = (char *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100374 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scull4f170f52018-07-19 12:58:20 +0100375 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
376 sizeof(struct fdt_reserve_entry));
377 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
378 sizeof(struct fdt_reserve_entry));
379 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
380 sizeof(struct fdt_reserve_entry));
381 memmove(begin + sizeof(struct fdt_reserve_entry), begin,
382 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap));
Andrew Scull29d40392018-07-16 11:46:59 +0100383 e->address = htobe64(addr);
384 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100385}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100386
387size_t fdt_header_size(void)
388{
389 return sizeof(struct fdt_header);
390}
391
392size_t fdt_total_size(struct fdt_header *hdr)
393{
394 return be32toh(hdr->totalsize);
395}