blob: 85d1d0a5c49980e15a5978f25ee063540a162a9a [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
Andrew Scull3c351e92020-01-28 11:26:05 +000061#define FDT_PROPERTY_NAME_MAX_SIZE 32
62
David Brazdil7a462ec2019-08-15 12:27:47 +010063#define FDT_TOKEN_ALIGNMENT sizeof(uint32_t)
64
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010065static void fdt_tokenizer_init(struct fdt_tokenizer *t, const char *strs,
66 const char *begin, const char *end)
67{
68 t->strs = strs;
69 t->cur = begin;
70 t->end = end;
71}
72
73static void fdt_tokenizer_align(struct fdt_tokenizer *t)
74{
David Brazdil7a462ec2019-08-15 12:27:47 +010075 t->cur = (char *)align_up(t->cur, FDT_TOKEN_ALIGNMENT);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010076}
77
78static bool fdt_tokenizer_uint32(struct fdt_tokenizer *t, uint32_t *res)
79{
80 const char *next = t->cur + sizeof(*res);
Andrew Scullcbefbdb2019-01-11 16:36:26 +000081
Andrew Scull7364a8e2018-07-19 15:39:29 +010082 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010083 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +010084 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010085
Andrew Scull29d40392018-07-16 11:46:59 +010086 *res = be32toh(*(uint32_t *)t->cur);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010087 t->cur = next;
88
89 return true;
90}
91
92static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
93{
94 uint32_t v;
95
96 while (fdt_tokenizer_uint32(t, &v)) {
97 if (v != FDT_NOP) {
98 *res = v;
99 return true;
100 }
101 }
102 return false;
103}
104
Andrew Scull4f170f52018-07-19 12:58:20 +0100105static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
106 size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100107{
108 const char *next = t->cur + size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000109
Andrew Scull7364a8e2018-07-19 15:39:29 +0100110 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100111 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100112 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100113
114 *res = t->cur;
115 t->cur = next;
116 fdt_tokenizer_align(t);
117
118 return true;
119}
120
121static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
122{
123 const char *p;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000124
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100125 for (p = t->cur; p < t->end; p++) {
126 if (!*p) {
127 /* Found the end of the string. */
128 *res = t->cur;
129 t->cur = p + 1;
130 fdt_tokenizer_align(t);
131 return true;
132 }
133 }
134
135 return false;
136}
137
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100138bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100139{
140 uint32_t max_ver;
141 uint32_t min_ver;
Andrew Scull29d40392018-07-16 11:46:59 +0100142 uint32_t begin = be32toh(hdr->off_dt_struct);
143 uint32_t size = be32toh(hdr->size_dt_struct);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100144
Andrew Scull2b5fbad2019-04-05 13:55:56 +0100145 memset_s(node, sizeof(*node), 0, sizeof(*node));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100146
147 /* Check the magic number before anything else. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100148 if (hdr->magic != be32toh(FDT_MAGIC)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100149 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100150 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100151
152 /* Check the version. */
Andrew Scull29d40392018-07-16 11:46:59 +0100153 max_ver = be32toh(hdr->version);
154 min_ver = be32toh(hdr->last_comp_version);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100155 if (FDT_VERSION < min_ver || FDT_VERSION > max_ver) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100156 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100157 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100158
159 /* TODO: Verify that it is all within the fdt. */
160 node->begin = (const char *)hdr + begin;
161 node->end = node->begin + size;
162
163 /* TODO: Verify strings as well. */
Andrew Scull29d40392018-07-16 11:46:59 +0100164 node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100165
166 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100167}
168
169static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
170 const char **buf, uint32_t *size)
171{
172 uint32_t token;
173 uint32_t nameoff;
174
Andrew Scull7364a8e2018-07-19 15:39:29 +0100175 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100176 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100177 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100178
179 if (token != FDT_PROP) {
180 /* Rewind so that caller will get the same token. */
181 t->cur -= sizeof(uint32_t);
182 return false;
183 }
184
185 if (!fdt_tokenizer_uint32(t, size) ||
186 !fdt_tokenizer_uint32(t, &nameoff) ||
187 !fdt_tokenizer_bytes(t, buf, *size)) {
188 /*
189 * Move cursor to the end so that caller won't get any new
190 * tokens.
191 */
192 t->cur = t->end;
193 return false;
194 }
195
196 /* TODO: Need to verify the strings. */
197 *name = t->strs + nameoff;
198
199 return true;
200}
201
202static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
203{
204 uint32_t token;
205
Andrew Scull7364a8e2018-07-19 15:39:29 +0100206 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100207 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100208 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100209
210 if (token != FDT_BEGIN_NODE) {
211 /* Rewind so that caller will get the same token. */
212 t->cur -= sizeof(uint32_t);
213 return false;
214 }
215
216 if (!fdt_tokenizer_str(t, name)) {
217 /*
218 * Move cursor to the end so that caller won't get any new
219 * tokens.
220 */
221 t->cur = t->end;
222 return false;
223 }
224
225 return true;
226}
227
228static void fdt_skip_properties(struct fdt_tokenizer *t)
229{
230 const char *name;
231 const char *buf;
232 uint32_t size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000233
Andrew Scull7364a8e2018-07-19 15:39:29 +0100234 while (fdt_next_property(t, &name, &buf, &size)) {
235 /* do nothing */
236 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100237}
238
239static bool fdt_skip_node(struct fdt_tokenizer *t)
240{
241 const char *name;
242 uint32_t token;
243 size_t pending = 1;
244
245 fdt_skip_properties(t);
246
247 do {
248 while (fdt_next_subnode(t, &name)) {
249 fdt_skip_properties(t);
250 pending++;
251 }
252
Andrew Scull7364a8e2018-07-19 15:39:29 +0100253 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100254 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100255 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100256
257 if (token != FDT_END_NODE) {
258 t->cur = t->end;
259 return false;
260 }
261
262 pending--;
263 } while (pending);
264
265 return true;
266}
267
268bool fdt_read_property(const struct fdt_node *node, const char *name,
269 const char **buf, uint32_t *size)
270{
271 struct fdt_tokenizer t;
272 const char *prop_name;
273
274 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
275
276 while (fdt_next_property(&t, &prop_name, buf, size)) {
Andrew Scull3c351e92020-01-28 11:26:05 +0000277 if (!strncmp(prop_name, name, FDT_PROPERTY_NAME_MAX_SIZE)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100278 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100279 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100280 }
281
282 return false;
283}
284
David Brazdil7a462ec2019-08-15 12:27:47 +0100285/**
286 * Helper method for parsing 32/64-bit uints from FDT data.
287 */
288bool fdt_parse_number(const char *data, uint32_t size, uint64_t *value)
289{
290 union {
291 volatile uint64_t v;
292 char a[8];
293 } t;
294
295 /* FDT values should be aligned to 32-bit boundary. */
296 CHECK(is_aligned(data, FDT_TOKEN_ALIGNMENT));
297
298 switch (size) {
299 case sizeof(uint32_t):
300 /*
301 * Assert that `data` is already sufficiently aligned to
302 * dereference as uint32_t. We cannot use static_assert()
303 * because alignof() is not an expression under ISO C11.
304 */
305 CHECK(alignof(uint32_t) <= FDT_TOKEN_ALIGNMENT);
306 *value = be32toh(*(uint32_t *)data);
307 return true;
308 case sizeof(uint64_t):
309 /*
Andrew Walbran20215742019-11-18 11:35:05 +0000310 * Armv8 requires `data` to be realigned to 64-bit boundary
David Brazdil7a462ec2019-08-15 12:27:47 +0100311 * to dereference as uint64_t. May not be needed on other
312 * architectures.
313 */
314 memcpy_s(t.a, sizeof(t.a), data, sizeof(uint64_t));
315 *value = be64toh(t.v);
316 return true;
317 default:
318 return false;
319 }
320}
321
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100322bool fdt_first_child(struct fdt_node *node, const char **child_name)
323{
324 struct fdt_tokenizer t;
325
326 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
327
328 fdt_skip_properties(&t);
329
Andrew Scull7364a8e2018-07-19 15:39:29 +0100330 if (!fdt_next_subnode(&t, child_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100331 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100332 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100333
334 node->begin = t.cur;
335
336 return true;
337}
338
339bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
340{
341 struct fdt_tokenizer t;
342
343 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
344
Andrew Scull7364a8e2018-07-19 15:39:29 +0100345 if (!fdt_skip_node(&t)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100346 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100347 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100348
Andrew Scull7364a8e2018-07-19 15:39:29 +0100349 if (!fdt_next_subnode(&t, sibling_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100350 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100351 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100352
353 node->begin = t.cur;
354
355 return true;
356}
357
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100358bool fdt_find_child(struct fdt_node *node, const char *child)
359{
360 struct fdt_tokenizer t;
361 const char *name;
362
363 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
364
365 fdt_skip_properties(&t);
366
367 while (fdt_next_subnode(&t, &name)) {
Andrew Scull3c351e92020-01-28 11:26:05 +0000368 if (!strncmp(name, child, FDT_PROPERTY_NAME_MAX_SIZE)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100369 node->begin = t.cur;
370 return true;
371 }
372
373 fdt_skip_node(&t);
374 }
375
376 return false;
377}
378
David Brazdil7a462ec2019-08-15 12:27:47 +0100379void fdt_dump(const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100380{
381 uint32_t token;
382 size_t depth = 0;
383 const char *name;
384 struct fdt_tokenizer t;
385 struct fdt_node node;
386
387 /* Traverse the whole thing. */
Andrew Scull83644292018-10-05 22:38:46 +0100388 if (!fdt_root_node(&node, hdr)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000389 dlog_error("FDT failed validation.\n");
Andrew Scull83644292018-10-05 22:38:46 +0100390 return;
391 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100392
393 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
394
395 do {
396 while (fdt_next_subnode(&t, &name)) {
397 const char *buf;
398 uint32_t size;
399
400 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
401 depth++;
402 while (fdt_next_property(&t, &name, &buf, &size)) {
Andrew Scull37402872018-10-24 14:23:06 +0100403 uint32_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000404
Andrew Scull4f170f52018-07-19 12:58:20 +0100405 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
406 name);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100407 for (i = 0; i < size; i++) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100408 dlog("%s%02x", i == 0 ? "" : " ",
409 buf[i]);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100410 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100411 dlog(")\n");
412 }
413 }
414
Andrew Scull7364a8e2018-07-19 15:39:29 +0100415 if (!fdt_tokenizer_token(&t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100416 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100417 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100418
Andrew Scull7364a8e2018-07-19 15:39:29 +0100419 if (token != FDT_END_NODE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100420 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100421 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100422
423 depth--;
424 } while (depth);
425
Andrew Scull29d40392018-07-16 11:46:59 +0100426 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100427 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100428 struct fdt_reserve_entry *e =
429 (struct fdt_reserve_entry
Andrew Scull37402872018-10-24 14:23:06 +0100430 *)((uintptr_t)hdr +
431 be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100432 while (e->address || e->size) {
Andrew Walbranac5b2612019-07-12 16:44:19 +0100433 dlog("Entry: %p (%#x bytes)\n", be64toh(e->address),
Andrew Scull4f170f52018-07-19 12:58:20 +0100434 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100435 e++;
436 }
437 }
438}
439
Andrew Scull37402872018-10-24 14:23:06 +0100440void fdt_add_mem_reservation(struct fdt_header *hdr, uint64_t addr,
441 uint64_t len)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100442{
443 /* TODO: Clean this up. */
Andrew Scull37402872018-10-24 14:23:06 +0100444 uint8_t *begin = (uint8_t *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100445 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scull8fbd7ee2019-04-05 14:36:34 +0100446 size_t old_size =
447 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap);
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000448
Andrew Scull4f170f52018-07-19 12:58:20 +0100449 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
450 sizeof(struct fdt_reserve_entry));
451 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
452 sizeof(struct fdt_reserve_entry));
453 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
454 sizeof(struct fdt_reserve_entry));
Andrew Scull8fbd7ee2019-04-05 14:36:34 +0100455 memmove_s(begin + sizeof(struct fdt_reserve_entry), old_size, begin,
456 old_size);
Andrew Scull29d40392018-07-16 11:46:59 +0100457 e->address = htobe64(addr);
458 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100459}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100460
461size_t fdt_header_size(void)
462{
463 return sizeof(struct fdt_header);
464}
465
David Brazdila2358d42020-01-27 18:51:38 +0000466uint32_t fdt_total_size(const struct fdt_header *hdr)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100467{
468 return be32toh(hdr->totalsize);
469}