blob: 162b6063b85b2d6039dbef5285c24aef87f192b7 [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);
59 if (next > t->end)
60 return false;
61
Andrew Scull29d40392018-07-16 11:46:59 +010062 *res = be32toh(*(uint32_t *)t->cur);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010063 t->cur = next;
64
65 return true;
66}
67
68static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
69{
70 uint32_t v;
71
72 while (fdt_tokenizer_uint32(t, &v)) {
73 if (v != FDT_NOP) {
74 *res = v;
75 return true;
76 }
77 }
78 return false;
79}
80
Andrew Scull4f170f52018-07-19 12:58:20 +010081static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
82 size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010083{
84 const char *next = t->cur + size;
85 if (next > t->end)
86 return false;
87
88 *res = t->cur;
89 t->cur = next;
90 fdt_tokenizer_align(t);
91
92 return true;
93}
94
95static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
96{
97 const char *p;
98 for (p = t->cur; p < t->end; p++) {
99 if (!*p) {
100 /* Found the end of the string. */
101 *res = t->cur;
102 t->cur = p + 1;
103 fdt_tokenizer_align(t);
104 return true;
105 }
106 }
107
108 return false;
109}
110
111void fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
112{
113 uint32_t max_ver;
114 uint32_t min_ver;
Andrew Scull29d40392018-07-16 11:46:59 +0100115 uint32_t begin = be32toh(hdr->off_dt_struct);
116 uint32_t size = be32toh(hdr->size_dt_struct);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100117
118 memset(node, 0, sizeof(*node));
119
120 /* Check the magic number before anything else. */
Andrew Scull29d40392018-07-16 11:46:59 +0100121 if (hdr->magic != be32toh(FDT_MAGIC))
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100122 return;
123
124 /* Check the version. */
Andrew Scull29d40392018-07-16 11:46:59 +0100125 max_ver = be32toh(hdr->version);
126 min_ver = be32toh(hdr->last_comp_version);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100127 if (FDT_VERSION < min_ver || FDT_VERSION > max_ver)
128 return;
129
130 /* TODO: Verify that it is all within the fdt. */
131 node->begin = (const char *)hdr + begin;
132 node->end = node->begin + size;
133
134 /* TODO: Verify strings as well. */
Andrew Scull29d40392018-07-16 11:46:59 +0100135 node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100136}
137
138static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
139 const char **buf, uint32_t *size)
140{
141 uint32_t token;
142 uint32_t nameoff;
143
144 if (!fdt_tokenizer_token(t, &token))
145 return false;
146
147 if (token != FDT_PROP) {
148 /* Rewind so that caller will get the same token. */
149 t->cur -= sizeof(uint32_t);
150 return false;
151 }
152
153 if (!fdt_tokenizer_uint32(t, size) ||
154 !fdt_tokenizer_uint32(t, &nameoff) ||
155 !fdt_tokenizer_bytes(t, buf, *size)) {
156 /*
157 * Move cursor to the end so that caller won't get any new
158 * tokens.
159 */
160 t->cur = t->end;
161 return false;
162 }
163
164 /* TODO: Need to verify the strings. */
165 *name = t->strs + nameoff;
166
167 return true;
168}
169
170static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
171{
172 uint32_t token;
173
174 if (!fdt_tokenizer_token(t, &token))
175 return false;
176
177 if (token != FDT_BEGIN_NODE) {
178 /* Rewind so that caller will get the same token. */
179 t->cur -= sizeof(uint32_t);
180 return false;
181 }
182
183 if (!fdt_tokenizer_str(t, name)) {
184 /*
185 * Move cursor to the end so that caller won't get any new
186 * tokens.
187 */
188 t->cur = t->end;
189 return false;
190 }
191
192 return true;
193}
194
195static void fdt_skip_properties(struct fdt_tokenizer *t)
196{
197 const char *name;
198 const char *buf;
199 uint32_t size;
Andrew Scull4f170f52018-07-19 12:58:20 +0100200 while (fdt_next_property(t, &name, &buf, &size))
201 ;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100202}
203
204static bool fdt_skip_node(struct fdt_tokenizer *t)
205{
206 const char *name;
207 uint32_t token;
208 size_t pending = 1;
209
210 fdt_skip_properties(t);
211
212 do {
213 while (fdt_next_subnode(t, &name)) {
214 fdt_skip_properties(t);
215 pending++;
216 }
217
218 if (!fdt_tokenizer_token(t, &token))
219 return false;
220
221 if (token != FDT_END_NODE) {
222 t->cur = t->end;
223 return false;
224 }
225
226 pending--;
227 } while (pending);
228
229 return true;
230}
231
232bool fdt_read_property(const struct fdt_node *node, const char *name,
233 const char **buf, uint32_t *size)
234{
235 struct fdt_tokenizer t;
236 const char *prop_name;
237
238 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
239
240 while (fdt_next_property(&t, &prop_name, buf, size)) {
241 if (!strcmp(prop_name, name))
242 return true;
243 }
244
245 return false;
246}
247
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100248bool fdt_first_child(struct fdt_node *node, const char **child_name)
249{
250 struct fdt_tokenizer t;
251
252 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
253
254 fdt_skip_properties(&t);
255
256 if (!fdt_next_subnode(&t, child_name))
257 return false;
258
259 node->begin = t.cur;
260
261 return true;
262}
263
264bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
265{
266 struct fdt_tokenizer t;
267
268 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
269
270 if (!fdt_skip_node(&t))
271 return false;
272
273 if (!fdt_next_subnode(&t, sibling_name))
274 return false;
275
276 node->begin = t.cur;
277
278 return true;
279}
280
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100281bool fdt_find_child(struct fdt_node *node, const char *child)
282{
283 struct fdt_tokenizer t;
284 const char *name;
285
286 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
287
288 fdt_skip_properties(&t);
289
290 while (fdt_next_subnode(&t, &name)) {
291 if (!strcmp(name, child)) {
292 node->begin = t.cur;
293 return true;
294 }
295
296 fdt_skip_node(&t);
297 }
298
299 return false;
300}
301
302void fdt_dump(struct fdt_header *hdr)
303{
304 uint32_t token;
305 size_t depth = 0;
306 const char *name;
307 struct fdt_tokenizer t;
308 struct fdt_node node;
309
310 /* Traverse the whole thing. */
311 fdt_root_node(&node, hdr);
312
313 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
314
315 do {
316 while (fdt_next_subnode(&t, &name)) {
317 const char *buf;
318 uint32_t size;
319
320 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
321 depth++;
322 while (fdt_next_property(&t, &name, &buf, &size)) {
323 size_t i;
Andrew Scull4f170f52018-07-19 12:58:20 +0100324 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
325 name);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100326 for (i = 0; i < size; i++)
Andrew Scull4f170f52018-07-19 12:58:20 +0100327 dlog("%s%02x", i == 0 ? "" : " ",
328 buf[i]);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100329 dlog(")\n");
330 }
331 }
332
333 if (!fdt_tokenizer_token(&t, &token))
334 return;
335
336 if (token != FDT_END_NODE)
337 return;
338
339 depth--;
340 } while (depth);
341
Andrew Scull29d40392018-07-16 11:46:59 +0100342 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100343 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100344 struct fdt_reserve_entry *e =
345 (struct fdt_reserve_entry
346 *)((size_t)hdr + be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100347 while (e->address || e->size) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100348 dlog("Entry: %p (0x%x bytes)\n", be64toh(e->address),
349 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100350 e++;
351 }
352 }
353}
354
355void fdt_add_mem_reservation(struct fdt_header *hdr, size_t addr, size_t len)
356{
357 /* TODO: Clean this up. */
Andrew Scull29d40392018-07-16 11:46:59 +0100358 char *begin = (char *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100359 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scull4f170f52018-07-19 12:58:20 +0100360 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
361 sizeof(struct fdt_reserve_entry));
362 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
363 sizeof(struct fdt_reserve_entry));
364 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
365 sizeof(struct fdt_reserve_entry));
366 memmove(begin + sizeof(struct fdt_reserve_entry), begin,
367 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap));
Andrew Scull29d40392018-07-16 11:46:59 +0100368 e->address = htobe64(addr);
369 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100370}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100371
372size_t fdt_header_size(void)
373{
374 return sizeof(struct fdt_header);
375}
376
377size_t fdt_total_size(struct fdt_header *hdr)
378{
379 return be32toh(hdr->totalsize);
380}