blob: 3f7804eab988de99a1e133db2f719c9750fcbd08 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/fdt.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010018
19#include <stdint.h>
20
Andrew Scull18c78fc2018-08-20 12:57:41 +010021#include "hf/dlog.h"
22#include "hf/std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010023
24struct fdt_header {
25 uint32_t magic;
26 uint32_t totalsize;
27 uint32_t off_dt_struct;
28 uint32_t off_dt_strings;
29 uint32_t off_mem_rsvmap;
30 uint32_t version;
31 uint32_t last_comp_version;
32 uint32_t boot_cpuid_phys;
33 uint32_t size_dt_strings;
34 uint32_t size_dt_struct;
35};
36
37struct fdt_reserve_entry {
38 uint64_t address;
39 uint64_t size;
40};
41
42enum fdt_token {
43 FDT_BEGIN_NODE = 1,
44 FDT_END_NODE = 2,
45 FDT_PROP = 3,
46 FDT_NOP = 4,
47 FDT_END = 9,
48};
49
50struct fdt_tokenizer {
51 const char *cur;
52 const char *end;
53 const char *strs;
54};
55
56#define FDT_VERSION 17
57#define FDT_MAGIC 0xd00dfeed
58
59static void fdt_tokenizer_init(struct fdt_tokenizer *t, const char *strs,
60 const char *begin, const char *end)
61{
62 t->strs = strs;
63 t->cur = begin;
64 t->end = end;
65}
66
67static void fdt_tokenizer_align(struct fdt_tokenizer *t)
68{
Andrew Scullf3d45592018-09-20 14:30:22 +010069 t->cur = (char *)(((uintptr_t)t->cur + 3) & ~3);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010070}
71
72static bool fdt_tokenizer_uint32(struct fdt_tokenizer *t, uint32_t *res)
73{
74 const char *next = t->cur + sizeof(*res);
Andrew Scull7364a8e2018-07-19 15:39:29 +010075 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010076 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +010077 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010078
Andrew Scull29d40392018-07-16 11:46:59 +010079 *res = be32toh(*(uint32_t *)t->cur);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010080 t->cur = next;
81
82 return true;
83}
84
85static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
86{
87 uint32_t v;
88
89 while (fdt_tokenizer_uint32(t, &v)) {
90 if (v != FDT_NOP) {
91 *res = v;
92 return true;
93 }
94 }
95 return false;
96}
97
Andrew Scull4f170f52018-07-19 12:58:20 +010098static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
99 size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100100{
101 const char *next = t->cur + size;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100102 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100103 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100104 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100105
106 *res = t->cur;
107 t->cur = next;
108 fdt_tokenizer_align(t);
109
110 return true;
111}
112
113static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
114{
115 const char *p;
116 for (p = t->cur; p < t->end; p++) {
117 if (!*p) {
118 /* Found the end of the string. */
119 *res = t->cur;
120 t->cur = p + 1;
121 fdt_tokenizer_align(t);
122 return true;
123 }
124 }
125
126 return false;
127}
128
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100129bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100130{
131 uint32_t max_ver;
132 uint32_t min_ver;
Andrew Scull29d40392018-07-16 11:46:59 +0100133 uint32_t begin = be32toh(hdr->off_dt_struct);
134 uint32_t size = be32toh(hdr->size_dt_struct);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100135
136 memset(node, 0, sizeof(*node));
137
138 /* Check the magic number before anything else. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100139 if (hdr->magic != be32toh(FDT_MAGIC)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100140 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100141 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100142
143 /* Check the version. */
Andrew Scull29d40392018-07-16 11:46:59 +0100144 max_ver = be32toh(hdr->version);
145 min_ver = be32toh(hdr->last_comp_version);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100146 if (FDT_VERSION < min_ver || FDT_VERSION > max_ver) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100147 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100148 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100149
150 /* TODO: Verify that it is all within the fdt. */
151 node->begin = (const char *)hdr + begin;
152 node->end = node->begin + size;
153
154 /* TODO: Verify strings as well. */
Andrew Scull29d40392018-07-16 11:46:59 +0100155 node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100156
157 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100158}
159
160static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
161 const char **buf, uint32_t *size)
162{
163 uint32_t token;
164 uint32_t nameoff;
165
Andrew Scull7364a8e2018-07-19 15:39:29 +0100166 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100167 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100168 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100169
170 if (token != FDT_PROP) {
171 /* Rewind so that caller will get the same token. */
172 t->cur -= sizeof(uint32_t);
173 return false;
174 }
175
176 if (!fdt_tokenizer_uint32(t, size) ||
177 !fdt_tokenizer_uint32(t, &nameoff) ||
178 !fdt_tokenizer_bytes(t, buf, *size)) {
179 /*
180 * Move cursor to the end so that caller won't get any new
181 * tokens.
182 */
183 t->cur = t->end;
184 return false;
185 }
186
187 /* TODO: Need to verify the strings. */
188 *name = t->strs + nameoff;
189
190 return true;
191}
192
193static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
194{
195 uint32_t token;
196
Andrew Scull7364a8e2018-07-19 15:39:29 +0100197 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100198 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100199 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100200
201 if (token != FDT_BEGIN_NODE) {
202 /* Rewind so that caller will get the same token. */
203 t->cur -= sizeof(uint32_t);
204 return false;
205 }
206
207 if (!fdt_tokenizer_str(t, name)) {
208 /*
209 * Move cursor to the end so that caller won't get any new
210 * tokens.
211 */
212 t->cur = t->end;
213 return false;
214 }
215
216 return true;
217}
218
219static void fdt_skip_properties(struct fdt_tokenizer *t)
220{
221 const char *name;
222 const char *buf;
223 uint32_t size;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100224 while (fdt_next_property(t, &name, &buf, &size)) {
225 /* do nothing */
226 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100227}
228
229static bool fdt_skip_node(struct fdt_tokenizer *t)
230{
231 const char *name;
232 uint32_t token;
233 size_t pending = 1;
234
235 fdt_skip_properties(t);
236
237 do {
238 while (fdt_next_subnode(t, &name)) {
239 fdt_skip_properties(t);
240 pending++;
241 }
242
Andrew Scull7364a8e2018-07-19 15:39:29 +0100243 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100244 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100245 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100246
247 if (token != FDT_END_NODE) {
248 t->cur = t->end;
249 return false;
250 }
251
252 pending--;
253 } while (pending);
254
255 return true;
256}
257
258bool fdt_read_property(const struct fdt_node *node, const char *name,
259 const char **buf, uint32_t *size)
260{
261 struct fdt_tokenizer t;
262 const char *prop_name;
263
264 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
265
266 while (fdt_next_property(&t, &prop_name, buf, size)) {
Andrew Scull7364a8e2018-07-19 15:39:29 +0100267 if (!strcmp(prop_name, name)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100268 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100269 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100270 }
271
272 return false;
273}
274
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100275bool fdt_first_child(struct fdt_node *node, const char **child_name)
276{
277 struct fdt_tokenizer t;
278
279 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
280
281 fdt_skip_properties(&t);
282
Andrew Scull7364a8e2018-07-19 15:39:29 +0100283 if (!fdt_next_subnode(&t, child_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100284 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100285 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100286
287 node->begin = t.cur;
288
289 return true;
290}
291
292bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
293{
294 struct fdt_tokenizer t;
295
296 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
297
Andrew Scull7364a8e2018-07-19 15:39:29 +0100298 if (!fdt_skip_node(&t)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100299 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100300 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100301
Andrew Scull7364a8e2018-07-19 15:39:29 +0100302 if (!fdt_next_subnode(&t, sibling_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100303 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100304 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100305
306 node->begin = t.cur;
307
308 return true;
309}
310
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100311bool fdt_find_child(struct fdt_node *node, const char *child)
312{
313 struct fdt_tokenizer t;
314 const char *name;
315
316 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
317
318 fdt_skip_properties(&t);
319
320 while (fdt_next_subnode(&t, &name)) {
321 if (!strcmp(name, child)) {
322 node->begin = t.cur;
323 return true;
324 }
325
326 fdt_skip_node(&t);
327 }
328
329 return false;
330}
331
332void fdt_dump(struct fdt_header *hdr)
333{
334 uint32_t token;
335 size_t depth = 0;
336 const char *name;
337 struct fdt_tokenizer t;
338 struct fdt_node node;
339
340 /* Traverse the whole thing. */
Andrew Scull83644292018-10-05 22:38:46 +0100341 if (!fdt_root_node(&node, hdr)) {
342 dlog("FDT failed validation.\n");
343 return;
344 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100345
346 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
347
348 do {
349 while (fdt_next_subnode(&t, &name)) {
350 const char *buf;
351 uint32_t size;
352
353 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
354 depth++;
355 while (fdt_next_property(&t, &name, &buf, &size)) {
356 size_t i;
Andrew Scull4f170f52018-07-19 12:58:20 +0100357 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
358 name);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100359 for (i = 0; i < size; i++) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100360 dlog("%s%02x", i == 0 ? "" : " ",
361 buf[i]);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100362 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100363 dlog(")\n");
364 }
365 }
366
Andrew Scull7364a8e2018-07-19 15:39:29 +0100367 if (!fdt_tokenizer_token(&t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100368 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100369 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100370
Andrew Scull7364a8e2018-07-19 15:39:29 +0100371 if (token != FDT_END_NODE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100372 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100373 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100374
375 depth--;
376 } while (depth);
377
Andrew Scull29d40392018-07-16 11:46:59 +0100378 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100379 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100380 struct fdt_reserve_entry *e =
381 (struct fdt_reserve_entry
382 *)((size_t)hdr + be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100383 while (e->address || e->size) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100384 dlog("Entry: %p (0x%x bytes)\n", be64toh(e->address),
385 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100386 e++;
387 }
388 }
389}
390
391void fdt_add_mem_reservation(struct fdt_header *hdr, size_t addr, size_t len)
392{
393 /* TODO: Clean this up. */
Andrew Scull29d40392018-07-16 11:46:59 +0100394 char *begin = (char *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100395 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scull4f170f52018-07-19 12:58:20 +0100396 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
397 sizeof(struct fdt_reserve_entry));
398 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
399 sizeof(struct fdt_reserve_entry));
400 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
401 sizeof(struct fdt_reserve_entry));
402 memmove(begin + sizeof(struct fdt_reserve_entry), begin,
403 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap));
Andrew Scull29d40392018-07-16 11:46:59 +0100404 e->address = htobe64(addr);
405 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100406}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100407
408size_t fdt_header_size(void)
409{
410 return sizeof(struct fdt_header);
411}
412
413size_t fdt_total_size(struct fdt_header *hdr)
414{
415 return be32toh(hdr->totalsize);
416}