blob: faf42b33f8602193bec9a8f0d7fd9ccb115bc841 [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
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100113bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100114{
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 Filhofdf4afc2018-07-19 15:45:21 +0100124 return false;
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 Filhofdf4afc2018-07-19 15:45:21 +0100131 return false;
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 Filhofdf4afc2018-07-19 15:45:21 +0100140
141 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100142}
143
144static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
145 const char **buf, uint32_t *size)
146{
147 uint32_t token;
148 uint32_t nameoff;
149
Andrew Scull7364a8e2018-07-19 15:39:29 +0100150 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100151 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100152 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100153
154 if (token != FDT_PROP) {
155 /* Rewind so that caller will get the same token. */
156 t->cur -= sizeof(uint32_t);
157 return false;
158 }
159
160 if (!fdt_tokenizer_uint32(t, size) ||
161 !fdt_tokenizer_uint32(t, &nameoff) ||
162 !fdt_tokenizer_bytes(t, buf, *size)) {
163 /*
164 * Move cursor to the end so that caller won't get any new
165 * tokens.
166 */
167 t->cur = t->end;
168 return false;
169 }
170
171 /* TODO: Need to verify the strings. */
172 *name = t->strs + nameoff;
173
174 return true;
175}
176
177static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
178{
179 uint32_t token;
180
Andrew Scull7364a8e2018-07-19 15:39:29 +0100181 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100182 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100183 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100184
185 if (token != FDT_BEGIN_NODE) {
186 /* Rewind so that caller will get the same token. */
187 t->cur -= sizeof(uint32_t);
188 return false;
189 }
190
191 if (!fdt_tokenizer_str(t, name)) {
192 /*
193 * Move cursor to the end so that caller won't get any new
194 * tokens.
195 */
196 t->cur = t->end;
197 return false;
198 }
199
200 return true;
201}
202
203static void fdt_skip_properties(struct fdt_tokenizer *t)
204{
205 const char *name;
206 const char *buf;
207 uint32_t size;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100208 while (fdt_next_property(t, &name, &buf, &size)) {
209 /* do nothing */
210 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100211}
212
213static bool fdt_skip_node(struct fdt_tokenizer *t)
214{
215 const char *name;
216 uint32_t token;
217 size_t pending = 1;
218
219 fdt_skip_properties(t);
220
221 do {
222 while (fdt_next_subnode(t, &name)) {
223 fdt_skip_properties(t);
224 pending++;
225 }
226
Andrew Scull7364a8e2018-07-19 15:39:29 +0100227 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100228 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100229 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100230
231 if (token != FDT_END_NODE) {
232 t->cur = t->end;
233 return false;
234 }
235
236 pending--;
237 } while (pending);
238
239 return true;
240}
241
242bool fdt_read_property(const struct fdt_node *node, const char *name,
243 const char **buf, uint32_t *size)
244{
245 struct fdt_tokenizer t;
246 const char *prop_name;
247
248 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
249
250 while (fdt_next_property(&t, &prop_name, buf, size)) {
Andrew Scull7364a8e2018-07-19 15:39:29 +0100251 if (!strcmp(prop_name, name)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100252 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100253 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254 }
255
256 return false;
257}
258
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100259bool fdt_first_child(struct fdt_node *node, const char **child_name)
260{
261 struct fdt_tokenizer t;
262
263 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
264
265 fdt_skip_properties(&t);
266
Andrew Scull7364a8e2018-07-19 15:39:29 +0100267 if (!fdt_next_subnode(&t, child_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100268 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100269 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100270
271 node->begin = t.cur;
272
273 return true;
274}
275
276bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
277{
278 struct fdt_tokenizer t;
279
280 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
281
Andrew Scull7364a8e2018-07-19 15:39:29 +0100282 if (!fdt_skip_node(&t)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100283 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100284 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100285
Andrew Scull7364a8e2018-07-19 15:39:29 +0100286 if (!fdt_next_subnode(&t, sibling_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100287 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100288 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100289
290 node->begin = t.cur;
291
292 return true;
293}
294
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100295bool fdt_find_child(struct fdt_node *node, const char *child)
296{
297 struct fdt_tokenizer t;
298 const char *name;
299
300 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
301
302 fdt_skip_properties(&t);
303
304 while (fdt_next_subnode(&t, &name)) {
305 if (!strcmp(name, child)) {
306 node->begin = t.cur;
307 return true;
308 }
309
310 fdt_skip_node(&t);
311 }
312
313 return false;
314}
315
316void fdt_dump(struct fdt_header *hdr)
317{
318 uint32_t token;
319 size_t depth = 0;
320 const char *name;
321 struct fdt_tokenizer t;
322 struct fdt_node node;
323
324 /* Traverse the whole thing. */
325 fdt_root_node(&node, hdr);
326
327 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
328
329 do {
330 while (fdt_next_subnode(&t, &name)) {
331 const char *buf;
332 uint32_t size;
333
334 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
335 depth++;
336 while (fdt_next_property(&t, &name, &buf, &size)) {
337 size_t i;
Andrew Scull4f170f52018-07-19 12:58:20 +0100338 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
339 name);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100340 for (i = 0; i < size; i++) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100341 dlog("%s%02x", i == 0 ? "" : " ",
342 buf[i]);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100343 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100344 dlog(")\n");
345 }
346 }
347
Andrew Scull7364a8e2018-07-19 15:39:29 +0100348 if (!fdt_tokenizer_token(&t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100349 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100350 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100351
Andrew Scull7364a8e2018-07-19 15:39:29 +0100352 if (token != FDT_END_NODE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100353 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100354 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100355
356 depth--;
357 } while (depth);
358
Andrew Scull29d40392018-07-16 11:46:59 +0100359 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100360 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100361 struct fdt_reserve_entry *e =
362 (struct fdt_reserve_entry
363 *)((size_t)hdr + be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100364 while (e->address || e->size) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100365 dlog("Entry: %p (0x%x bytes)\n", be64toh(e->address),
366 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100367 e++;
368 }
369 }
370}
371
372void fdt_add_mem_reservation(struct fdt_header *hdr, size_t addr, size_t len)
373{
374 /* TODO: Clean this up. */
Andrew Scull29d40392018-07-16 11:46:59 +0100375 char *begin = (char *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100376 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scull4f170f52018-07-19 12:58:20 +0100377 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
378 sizeof(struct fdt_reserve_entry));
379 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
380 sizeof(struct fdt_reserve_entry));
381 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
382 sizeof(struct fdt_reserve_entry));
383 memmove(begin + sizeof(struct fdt_reserve_entry), begin,
384 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap));
Andrew Scull29d40392018-07-16 11:46:59 +0100385 e->address = htobe64(addr);
386 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100387}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100388
389size_t fdt_header_size(void)
390{
391 return sizeof(struct fdt_header);
392}
393
394size_t fdt_total_size(struct fdt_header *hdr)
395{
396 return be32toh(hdr->totalsize);
397}