blob: de54189f4b1e25e1c1e801b52a84477ad39042b3 [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
19#include <stdint.h>
20
Andrew Scull18c78fc2018-08-20 12:57:41 +010021#include "hf/dlog.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010022#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{
Alfredo Mazzinghieb1997c2019-02-07 18:00:01 +000069 t->cur = (char *)align_up(t->cur, 4);
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 Scullcbefbdb2019-01-11 16:36:26 +000075
Andrew Scull7364a8e2018-07-19 15:39:29 +010076 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010077 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +010078 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010079
Andrew Scull29d40392018-07-16 11:46:59 +010080 *res = be32toh(*(uint32_t *)t->cur);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010081 t->cur = next;
82
83 return true;
84}
85
86static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
87{
88 uint32_t v;
89
90 while (fdt_tokenizer_uint32(t, &v)) {
91 if (v != FDT_NOP) {
92 *res = v;
93 return true;
94 }
95 }
96 return false;
97}
98
Andrew Scull4f170f52018-07-19 12:58:20 +010099static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
100 size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100101{
102 const char *next = t->cur + size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000103
Andrew Scull7364a8e2018-07-19 15:39:29 +0100104 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100105 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100106 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100107
108 *res = t->cur;
109 t->cur = next;
110 fdt_tokenizer_align(t);
111
112 return true;
113}
114
115static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
116{
117 const char *p;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000118
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100119 for (p = t->cur; p < t->end; p++) {
120 if (!*p) {
121 /* Found the end of the string. */
122 *res = t->cur;
123 t->cur = p + 1;
124 fdt_tokenizer_align(t);
125 return true;
126 }
127 }
128
129 return false;
130}
131
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100132bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100133{
134 uint32_t max_ver;
135 uint32_t min_ver;
Andrew Scull29d40392018-07-16 11:46:59 +0100136 uint32_t begin = be32toh(hdr->off_dt_struct);
137 uint32_t size = be32toh(hdr->size_dt_struct);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100138
Andrew Scull2b5fbad2019-04-05 13:55:56 +0100139 memset_s(node, sizeof(*node), 0, sizeof(*node));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100140
141 /* Check the magic number before anything else. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100142 if (hdr->magic != be32toh(FDT_MAGIC)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100143 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100144 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100145
146 /* Check the version. */
Andrew Scull29d40392018-07-16 11:46:59 +0100147 max_ver = be32toh(hdr->version);
148 min_ver = be32toh(hdr->last_comp_version);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100149 if (FDT_VERSION < min_ver || FDT_VERSION > max_ver) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100150 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100151 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100152
153 /* TODO: Verify that it is all within the fdt. */
154 node->begin = (const char *)hdr + begin;
155 node->end = node->begin + size;
156
157 /* TODO: Verify strings as well. */
Andrew Scull29d40392018-07-16 11:46:59 +0100158 node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100159
160 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100161}
162
163static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
164 const char **buf, uint32_t *size)
165{
166 uint32_t token;
167 uint32_t nameoff;
168
Andrew Scull7364a8e2018-07-19 15:39:29 +0100169 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100170 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100171 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100172
173 if (token != FDT_PROP) {
174 /* Rewind so that caller will get the same token. */
175 t->cur -= sizeof(uint32_t);
176 return false;
177 }
178
179 if (!fdt_tokenizer_uint32(t, size) ||
180 !fdt_tokenizer_uint32(t, &nameoff) ||
181 !fdt_tokenizer_bytes(t, buf, *size)) {
182 /*
183 * Move cursor to the end so that caller won't get any new
184 * tokens.
185 */
186 t->cur = t->end;
187 return false;
188 }
189
190 /* TODO: Need to verify the strings. */
191 *name = t->strs + nameoff;
192
193 return true;
194}
195
196static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
197{
198 uint32_t token;
199
Andrew Scull7364a8e2018-07-19 15:39:29 +0100200 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100201 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100202 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100203
204 if (token != FDT_BEGIN_NODE) {
205 /* Rewind so that caller will get the same token. */
206 t->cur -= sizeof(uint32_t);
207 return false;
208 }
209
210 if (!fdt_tokenizer_str(t, name)) {
211 /*
212 * Move cursor to the end so that caller won't get any new
213 * tokens.
214 */
215 t->cur = t->end;
216 return false;
217 }
218
219 return true;
220}
221
222static void fdt_skip_properties(struct fdt_tokenizer *t)
223{
224 const char *name;
225 const char *buf;
226 uint32_t size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000227
Andrew Scull7364a8e2018-07-19 15:39:29 +0100228 while (fdt_next_property(t, &name, &buf, &size)) {
229 /* do nothing */
230 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100231}
232
233static bool fdt_skip_node(struct fdt_tokenizer *t)
234{
235 const char *name;
236 uint32_t token;
237 size_t pending = 1;
238
239 fdt_skip_properties(t);
240
241 do {
242 while (fdt_next_subnode(t, &name)) {
243 fdt_skip_properties(t);
244 pending++;
245 }
246
Andrew Scull7364a8e2018-07-19 15:39:29 +0100247 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100248 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100249 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100250
251 if (token != FDT_END_NODE) {
252 t->cur = t->end;
253 return false;
254 }
255
256 pending--;
257 } while (pending);
258
259 return true;
260}
261
262bool fdt_read_property(const struct fdt_node *node, const char *name,
263 const char **buf, uint32_t *size)
264{
265 struct fdt_tokenizer t;
266 const char *prop_name;
267
268 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
269
270 while (fdt_next_property(&t, &prop_name, buf, size)) {
Andrew Scull7364a8e2018-07-19 15:39:29 +0100271 if (!strcmp(prop_name, name)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100272 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100273 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100274 }
275
276 return false;
277}
278
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100279bool fdt_first_child(struct fdt_node *node, const char **child_name)
280{
281 struct fdt_tokenizer t;
282
283 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
284
285 fdt_skip_properties(&t);
286
Andrew Scull7364a8e2018-07-19 15:39:29 +0100287 if (!fdt_next_subnode(&t, child_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100288 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100289 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100290
291 node->begin = t.cur;
292
293 return true;
294}
295
296bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
297{
298 struct fdt_tokenizer t;
299
300 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
301
Andrew Scull7364a8e2018-07-19 15:39:29 +0100302 if (!fdt_skip_node(&t)) {
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
Andrew Scull7364a8e2018-07-19 15:39:29 +0100306 if (!fdt_next_subnode(&t, sibling_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100307 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100308 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100309
310 node->begin = t.cur;
311
312 return true;
313}
314
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100315bool fdt_find_child(struct fdt_node *node, const char *child)
316{
317 struct fdt_tokenizer t;
318 const char *name;
319
320 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
321
322 fdt_skip_properties(&t);
323
324 while (fdt_next_subnode(&t, &name)) {
325 if (!strcmp(name, child)) {
326 node->begin = t.cur;
327 return true;
328 }
329
330 fdt_skip_node(&t);
331 }
332
333 return false;
334}
335
336void fdt_dump(struct fdt_header *hdr)
337{
338 uint32_t token;
339 size_t depth = 0;
340 const char *name;
341 struct fdt_tokenizer t;
342 struct fdt_node node;
343
344 /* Traverse the whole thing. */
Andrew Scull83644292018-10-05 22:38:46 +0100345 if (!fdt_root_node(&node, hdr)) {
346 dlog("FDT failed validation.\n");
347 return;
348 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100349
350 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
351
352 do {
353 while (fdt_next_subnode(&t, &name)) {
354 const char *buf;
355 uint32_t size;
356
357 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
358 depth++;
359 while (fdt_next_property(&t, &name, &buf, &size)) {
Andrew Scull37402872018-10-24 14:23:06 +0100360 uint32_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000361
Andrew Scull4f170f52018-07-19 12:58:20 +0100362 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
363 name);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100364 for (i = 0; i < size; i++) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100365 dlog("%s%02x", i == 0 ? "" : " ",
366 buf[i]);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100367 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100368 dlog(")\n");
369 }
370 }
371
Andrew Scull7364a8e2018-07-19 15:39:29 +0100372 if (!fdt_tokenizer_token(&t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100373 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100374 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100375
Andrew Scull7364a8e2018-07-19 15:39:29 +0100376 if (token != FDT_END_NODE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100377 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100378 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100379
380 depth--;
381 } while (depth);
382
Andrew Scull29d40392018-07-16 11:46:59 +0100383 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100384 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100385 struct fdt_reserve_entry *e =
386 (struct fdt_reserve_entry
Andrew Scull37402872018-10-24 14:23:06 +0100387 *)((uintptr_t)hdr +
388 be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100389 while (e->address || e->size) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100390 dlog("Entry: %p (0x%x bytes)\n", be64toh(e->address),
391 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100392 e++;
393 }
394 }
395}
396
Andrew Scull37402872018-10-24 14:23:06 +0100397void fdt_add_mem_reservation(struct fdt_header *hdr, uint64_t addr,
398 uint64_t len)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100399{
400 /* TODO: Clean this up. */
Andrew Scull37402872018-10-24 14:23:06 +0100401 uint8_t *begin = (uint8_t *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100402 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scull8fbd7ee2019-04-05 14:36:34 +0100403 size_t old_size =
404 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap);
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000405
Andrew Scull4f170f52018-07-19 12:58:20 +0100406 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
407 sizeof(struct fdt_reserve_entry));
408 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
409 sizeof(struct fdt_reserve_entry));
410 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
411 sizeof(struct fdt_reserve_entry));
Andrew Scull8fbd7ee2019-04-05 14:36:34 +0100412 memmove_s(begin + sizeof(struct fdt_reserve_entry), old_size, begin,
413 old_size);
Andrew Scull29d40392018-07-16 11:46:59 +0100414 e->address = htobe64(addr);
415 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100416}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100417
418size_t fdt_header_size(void)
419{
420 return sizeof(struct fdt_header);
421}
422
Andrew Scull37402872018-10-24 14:23:06 +0100423uint32_t fdt_total_size(struct fdt_header *hdr)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100424{
425 return be32toh(hdr->totalsize);
426}