blob: d9cd71d7e82c7dd69efaf8f9a0c2c4f1484b5b7d [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Andrew Scull18834872018-10-12 11:48:09 +01003 *
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
David Brazdil7a462ec2019-08-15 12:27:47 +010019#include <stdalign.h>
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010020#include <stdint.h>
21
David Brazdil7a462ec2019-08-15 12:27:47 +010022#include "hf/check.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/dlog.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010024#include "hf/std.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010025
26struct fdt_header {
27 uint32_t magic;
28 uint32_t totalsize;
29 uint32_t off_dt_struct;
30 uint32_t off_dt_strings;
31 uint32_t off_mem_rsvmap;
32 uint32_t version;
33 uint32_t last_comp_version;
34 uint32_t boot_cpuid_phys;
35 uint32_t size_dt_strings;
36 uint32_t size_dt_struct;
37};
38
39struct fdt_reserve_entry {
40 uint64_t address;
41 uint64_t size;
42};
43
44enum fdt_token {
45 FDT_BEGIN_NODE = 1,
46 FDT_END_NODE = 2,
47 FDT_PROP = 3,
48 FDT_NOP = 4,
49 FDT_END = 9,
50};
51
52struct fdt_tokenizer {
53 const char *cur;
54 const char *end;
55 const char *strs;
56};
57
58#define FDT_VERSION 17
59#define FDT_MAGIC 0xd00dfeed
60
David Brazdil7a462ec2019-08-15 12:27:47 +010061#define FDT_TOKEN_ALIGNMENT sizeof(uint32_t)
62
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010063static void fdt_tokenizer_init(struct fdt_tokenizer *t, const char *strs,
64 const char *begin, const char *end)
65{
66 t->strs = strs;
67 t->cur = begin;
68 t->end = end;
69}
70
71static void fdt_tokenizer_align(struct fdt_tokenizer *t)
72{
David Brazdil7a462ec2019-08-15 12:27:47 +010073 t->cur = (char *)align_up(t->cur, FDT_TOKEN_ALIGNMENT);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010074}
75
76static bool fdt_tokenizer_uint32(struct fdt_tokenizer *t, uint32_t *res)
77{
78 const char *next = t->cur + sizeof(*res);
Andrew Scullcbefbdb2019-01-11 16:36:26 +000079
Andrew Scull7364a8e2018-07-19 15:39:29 +010080 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010081 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +010082 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010083
Andrew Scull29d40392018-07-16 11:46:59 +010084 *res = be32toh(*(uint32_t *)t->cur);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010085 t->cur = next;
86
87 return true;
88}
89
90static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
91{
92 uint32_t v;
93
94 while (fdt_tokenizer_uint32(t, &v)) {
95 if (v != FDT_NOP) {
96 *res = v;
97 return true;
98 }
99 }
100 return false;
101}
102
Andrew Scull4f170f52018-07-19 12:58:20 +0100103static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
104 size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100105{
106 const char *next = t->cur + size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000107
Andrew Scull7364a8e2018-07-19 15:39:29 +0100108 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100109 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100110 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100111
112 *res = t->cur;
113 t->cur = next;
114 fdt_tokenizer_align(t);
115
116 return true;
117}
118
119static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
120{
121 const char *p;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000122
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100123 for (p = t->cur; p < t->end; p++) {
124 if (!*p) {
125 /* Found the end of the string. */
126 *res = t->cur;
127 t->cur = p + 1;
128 fdt_tokenizer_align(t);
129 return true;
130 }
131 }
132
133 return false;
134}
135
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100136bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100137{
138 uint32_t max_ver;
139 uint32_t min_ver;
Andrew Scull29d40392018-07-16 11:46:59 +0100140 uint32_t begin = be32toh(hdr->off_dt_struct);
141 uint32_t size = be32toh(hdr->size_dt_struct);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100142
Andrew Scull2b5fbad2019-04-05 13:55:56 +0100143 memset_s(node, sizeof(*node), 0, sizeof(*node));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100144
145 /* Check the magic number before anything else. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100146 if (hdr->magic != be32toh(FDT_MAGIC)) {
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 /* Check the version. */
Andrew Scull29d40392018-07-16 11:46:59 +0100151 max_ver = be32toh(hdr->version);
152 min_ver = be32toh(hdr->last_comp_version);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100153 if (FDT_VERSION < min_ver || FDT_VERSION > max_ver) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100154 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100155 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100156
157 /* TODO: Verify that it is all within the fdt. */
158 node->begin = (const char *)hdr + begin;
159 node->end = node->begin + size;
160
161 /* TODO: Verify strings as well. */
Andrew Scull29d40392018-07-16 11:46:59 +0100162 node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100163
164 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100165}
166
167static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
168 const char **buf, uint32_t *size)
169{
170 uint32_t token;
171 uint32_t nameoff;
172
Andrew Scull7364a8e2018-07-19 15:39:29 +0100173 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100174 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100175 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100176
177 if (token != FDT_PROP) {
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_uint32(t, size) ||
184 !fdt_tokenizer_uint32(t, &nameoff) ||
185 !fdt_tokenizer_bytes(t, buf, *size)) {
186 /*
187 * Move cursor to the end so that caller won't get any new
188 * tokens.
189 */
190 t->cur = t->end;
191 return false;
192 }
193
194 /* TODO: Need to verify the strings. */
195 *name = t->strs + nameoff;
196
197 return true;
198}
199
200static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
201{
202 uint32_t token;
203
Andrew Scull7364a8e2018-07-19 15:39:29 +0100204 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100205 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100206 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100207
208 if (token != FDT_BEGIN_NODE) {
209 /* Rewind so that caller will get the same token. */
210 t->cur -= sizeof(uint32_t);
211 return false;
212 }
213
214 if (!fdt_tokenizer_str(t, name)) {
215 /*
216 * Move cursor to the end so that caller won't get any new
217 * tokens.
218 */
219 t->cur = t->end;
220 return false;
221 }
222
223 return true;
224}
225
226static void fdt_skip_properties(struct fdt_tokenizer *t)
227{
228 const char *name;
229 const char *buf;
230 uint32_t size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000231
Andrew Scull7364a8e2018-07-19 15:39:29 +0100232 while (fdt_next_property(t, &name, &buf, &size)) {
233 /* do nothing */
234 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100235}
236
237static bool fdt_skip_node(struct fdt_tokenizer *t)
238{
239 const char *name;
240 uint32_t token;
241 size_t pending = 1;
242
243 fdt_skip_properties(t);
244
245 do {
246 while (fdt_next_subnode(t, &name)) {
247 fdt_skip_properties(t);
248 pending++;
249 }
250
Andrew Scull7364a8e2018-07-19 15:39:29 +0100251 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100252 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100253 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254
255 if (token != FDT_END_NODE) {
256 t->cur = t->end;
257 return false;
258 }
259
260 pending--;
261 } while (pending);
262
263 return true;
264}
265
266bool fdt_read_property(const struct fdt_node *node, const char *name,
267 const char **buf, uint32_t *size)
268{
269 struct fdt_tokenizer t;
270 const char *prop_name;
271
272 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
273
274 while (fdt_next_property(&t, &prop_name, buf, size)) {
Andrew Scull7364a8e2018-07-19 15:39:29 +0100275 if (!strcmp(prop_name, name)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100276 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100277 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100278 }
279
280 return false;
281}
282
David Brazdil7a462ec2019-08-15 12:27:47 +0100283/**
284 * Helper method for parsing 32/64-bit uints from FDT data.
285 */
286bool fdt_parse_number(const char *data, uint32_t size, uint64_t *value)
287{
288 union {
289 volatile uint64_t v;
290 char a[8];
291 } t;
292
293 /* FDT values should be aligned to 32-bit boundary. */
294 CHECK(is_aligned(data, FDT_TOKEN_ALIGNMENT));
295
296 switch (size) {
297 case sizeof(uint32_t):
298 /*
299 * Assert that `data` is already sufficiently aligned to
300 * dereference as uint32_t. We cannot use static_assert()
301 * because alignof() is not an expression under ISO C11.
302 */
303 CHECK(alignof(uint32_t) <= FDT_TOKEN_ALIGNMENT);
304 *value = be32toh(*(uint32_t *)data);
305 return true;
306 case sizeof(uint64_t):
307 /*
308 * ARMv8 requires `data` to be realigned to 64-bit boundary
309 * to dereference as uint64_t. May not be needed on other
310 * architectures.
311 */
312 memcpy_s(t.a, sizeof(t.a), data, sizeof(uint64_t));
313 *value = be64toh(t.v);
314 return true;
315 default:
316 return false;
317 }
318}
319
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100320bool fdt_first_child(struct fdt_node *node, const char **child_name)
321{
322 struct fdt_tokenizer t;
323
324 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
325
326 fdt_skip_properties(&t);
327
Andrew Scull7364a8e2018-07-19 15:39:29 +0100328 if (!fdt_next_subnode(&t, child_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100329 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100330 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100331
332 node->begin = t.cur;
333
334 return true;
335}
336
337bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
338{
339 struct fdt_tokenizer t;
340
341 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
342
Andrew Scull7364a8e2018-07-19 15:39:29 +0100343 if (!fdt_skip_node(&t)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100344 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100345 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100346
Andrew Scull7364a8e2018-07-19 15:39:29 +0100347 if (!fdt_next_subnode(&t, sibling_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100348 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100349 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100350
351 node->begin = t.cur;
352
353 return true;
354}
355
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100356bool fdt_find_child(struct fdt_node *node, const char *child)
357{
358 struct fdt_tokenizer t;
359 const char *name;
360
361 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
362
363 fdt_skip_properties(&t);
364
365 while (fdt_next_subnode(&t, &name)) {
366 if (!strcmp(name, child)) {
367 node->begin = t.cur;
368 return true;
369 }
370
371 fdt_skip_node(&t);
372 }
373
374 return false;
375}
376
David Brazdil7a462ec2019-08-15 12:27:47 +0100377void fdt_dump(const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100378{
379 uint32_t token;
380 size_t depth = 0;
381 const char *name;
382 struct fdt_tokenizer t;
383 struct fdt_node node;
384
385 /* Traverse the whole thing. */
Andrew Scull83644292018-10-05 22:38:46 +0100386 if (!fdt_root_node(&node, hdr)) {
387 dlog("FDT failed validation.\n");
388 return;
389 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100390
391 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
392
393 do {
394 while (fdt_next_subnode(&t, &name)) {
395 const char *buf;
396 uint32_t size;
397
398 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
399 depth++;
400 while (fdt_next_property(&t, &name, &buf, &size)) {
Andrew Scull37402872018-10-24 14:23:06 +0100401 uint32_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000402
Andrew Scull4f170f52018-07-19 12:58:20 +0100403 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
404 name);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100405 for (i = 0; i < size; i++) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100406 dlog("%s%02x", i == 0 ? "" : " ",
407 buf[i]);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100408 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100409 dlog(")\n");
410 }
411 }
412
Andrew Scull7364a8e2018-07-19 15:39:29 +0100413 if (!fdt_tokenizer_token(&t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100414 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100415 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100416
Andrew Scull7364a8e2018-07-19 15:39:29 +0100417 if (token != FDT_END_NODE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100418 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100419 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100420
421 depth--;
422 } while (depth);
423
Andrew Scull29d40392018-07-16 11:46:59 +0100424 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100425 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100426 struct fdt_reserve_entry *e =
427 (struct fdt_reserve_entry
Andrew Scull37402872018-10-24 14:23:06 +0100428 *)((uintptr_t)hdr +
429 be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100430 while (e->address || e->size) {
Andrew Walbranac5b2612019-07-12 16:44:19 +0100431 dlog("Entry: %p (%#x bytes)\n", be64toh(e->address),
Andrew Scull4f170f52018-07-19 12:58:20 +0100432 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100433 e++;
434 }
435 }
436}
437
Andrew Scull37402872018-10-24 14:23:06 +0100438void fdt_add_mem_reservation(struct fdt_header *hdr, uint64_t addr,
439 uint64_t len)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100440{
441 /* TODO: Clean this up. */
Andrew Scull37402872018-10-24 14:23:06 +0100442 uint8_t *begin = (uint8_t *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100443 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scull8fbd7ee2019-04-05 14:36:34 +0100444 size_t old_size =
445 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap);
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000446
Andrew Scull4f170f52018-07-19 12:58:20 +0100447 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
448 sizeof(struct fdt_reserve_entry));
449 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
450 sizeof(struct fdt_reserve_entry));
451 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
452 sizeof(struct fdt_reserve_entry));
Andrew Scull8fbd7ee2019-04-05 14:36:34 +0100453 memmove_s(begin + sizeof(struct fdt_reserve_entry), old_size, begin,
454 old_size);
Andrew Scull29d40392018-07-16 11:46:59 +0100455 e->address = htobe64(addr);
456 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100457}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100458
459size_t fdt_header_size(void)
460{
461 return sizeof(struct fdt_header);
462}
463
Andrew Scull37402872018-10-24 14:23:06 +0100464uint32_t fdt_total_size(struct fdt_header *hdr)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100465{
466 return be32toh(hdr->totalsize);
467}