blob: 3859b46c92919672db57701ee51a985e15792bae [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 Walbran4a53ba62019-03-05 17:26:12 +000021#include "hf/arch/std.h"
22
Andrew Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/dlog.h"
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010024
25struct fdt_header {
26 uint32_t magic;
27 uint32_t totalsize;
28 uint32_t off_dt_struct;
29 uint32_t off_dt_strings;
30 uint32_t off_mem_rsvmap;
31 uint32_t version;
32 uint32_t last_comp_version;
33 uint32_t boot_cpuid_phys;
34 uint32_t size_dt_strings;
35 uint32_t size_dt_struct;
36};
37
38struct fdt_reserve_entry {
39 uint64_t address;
40 uint64_t size;
41};
42
43enum fdt_token {
44 FDT_BEGIN_NODE = 1,
45 FDT_END_NODE = 2,
46 FDT_PROP = 3,
47 FDT_NOP = 4,
48 FDT_END = 9,
49};
50
51struct fdt_tokenizer {
52 const char *cur;
53 const char *end;
54 const char *strs;
55};
56
57#define FDT_VERSION 17
58#define FDT_MAGIC 0xd00dfeed
59
60static void fdt_tokenizer_init(struct fdt_tokenizer *t, const char *strs,
61 const char *begin, const char *end)
62{
63 t->strs = strs;
64 t->cur = begin;
65 t->end = end;
66}
67
68static void fdt_tokenizer_align(struct fdt_tokenizer *t)
69{
Alfredo Mazzinghieb1997c2019-02-07 18:00:01 +000070 t->cur = (char *)align_up(t->cur, 4);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010071}
72
73static bool fdt_tokenizer_uint32(struct fdt_tokenizer *t, uint32_t *res)
74{
75 const char *next = t->cur + sizeof(*res);
Andrew Scullcbefbdb2019-01-11 16:36:26 +000076
Andrew Scull7364a8e2018-07-19 15:39:29 +010077 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010078 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +010079 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010080
Andrew Scull29d40392018-07-16 11:46:59 +010081 *res = be32toh(*(uint32_t *)t->cur);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +010082 t->cur = next;
83
84 return true;
85}
86
87static bool fdt_tokenizer_token(struct fdt_tokenizer *t, uint32_t *res)
88{
89 uint32_t v;
90
91 while (fdt_tokenizer_uint32(t, &v)) {
92 if (v != FDT_NOP) {
93 *res = v;
94 return true;
95 }
96 }
97 return false;
98}
99
Andrew Scull4f170f52018-07-19 12:58:20 +0100100static bool fdt_tokenizer_bytes(struct fdt_tokenizer *t, const char **res,
101 size_t size)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100102{
103 const char *next = t->cur + size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000104
Andrew Scull7364a8e2018-07-19 15:39:29 +0100105 if (next > t->end) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100106 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100107 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100108
109 *res = t->cur;
110 t->cur = next;
111 fdt_tokenizer_align(t);
112
113 return true;
114}
115
116static bool fdt_tokenizer_str(struct fdt_tokenizer *t, const char **res)
117{
118 const char *p;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000119
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100120 for (p = t->cur; p < t->end; p++) {
121 if (!*p) {
122 /* Found the end of the string. */
123 *res = t->cur;
124 t->cur = p + 1;
125 fdt_tokenizer_align(t);
126 return true;
127 }
128 }
129
130 return false;
131}
132
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100133bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100134{
135 uint32_t max_ver;
136 uint32_t min_ver;
Andrew Scull29d40392018-07-16 11:46:59 +0100137 uint32_t begin = be32toh(hdr->off_dt_struct);
138 uint32_t size = be32toh(hdr->size_dt_struct);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100139
140 memset(node, 0, sizeof(*node));
141
142 /* Check the magic number before anything else. */
Andrew Scull7364a8e2018-07-19 15:39:29 +0100143 if (hdr->magic != be32toh(FDT_MAGIC)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100144 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100145 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100146
147 /* Check the version. */
Andrew Scull29d40392018-07-16 11:46:59 +0100148 max_ver = be32toh(hdr->version);
149 min_ver = be32toh(hdr->last_comp_version);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100150 if (FDT_VERSION < min_ver || FDT_VERSION > max_ver) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100151 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100152 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100153
154 /* TODO: Verify that it is all within the fdt. */
155 node->begin = (const char *)hdr + begin;
156 node->end = node->begin + size;
157
158 /* TODO: Verify strings as well. */
Andrew Scull29d40392018-07-16 11:46:59 +0100159 node->strs = (char *)hdr + be32toh(hdr->off_dt_strings);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100160
161 return true;
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100162}
163
164static bool fdt_next_property(struct fdt_tokenizer *t, const char **name,
165 const char **buf, uint32_t *size)
166{
167 uint32_t token;
168 uint32_t nameoff;
169
Andrew Scull7364a8e2018-07-19 15:39:29 +0100170 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100171 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100172 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100173
174 if (token != FDT_PROP) {
175 /* Rewind so that caller will get the same token. */
176 t->cur -= sizeof(uint32_t);
177 return false;
178 }
179
180 if (!fdt_tokenizer_uint32(t, size) ||
181 !fdt_tokenizer_uint32(t, &nameoff) ||
182 !fdt_tokenizer_bytes(t, buf, *size)) {
183 /*
184 * Move cursor to the end so that caller won't get any new
185 * tokens.
186 */
187 t->cur = t->end;
188 return false;
189 }
190
191 /* TODO: Need to verify the strings. */
192 *name = t->strs + nameoff;
193
194 return true;
195}
196
197static bool fdt_next_subnode(struct fdt_tokenizer *t, const char **name)
198{
199 uint32_t token;
200
Andrew Scull7364a8e2018-07-19 15:39:29 +0100201 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100202 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100203 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100204
205 if (token != FDT_BEGIN_NODE) {
206 /* Rewind so that caller will get the same token. */
207 t->cur -= sizeof(uint32_t);
208 return false;
209 }
210
211 if (!fdt_tokenizer_str(t, name)) {
212 /*
213 * Move cursor to the end so that caller won't get any new
214 * tokens.
215 */
216 t->cur = t->end;
217 return false;
218 }
219
220 return true;
221}
222
223static void fdt_skip_properties(struct fdt_tokenizer *t)
224{
225 const char *name;
226 const char *buf;
227 uint32_t size;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000228
Andrew Scull7364a8e2018-07-19 15:39:29 +0100229 while (fdt_next_property(t, &name, &buf, &size)) {
230 /* do nothing */
231 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100232}
233
234static bool fdt_skip_node(struct fdt_tokenizer *t)
235{
236 const char *name;
237 uint32_t token;
238 size_t pending = 1;
239
240 fdt_skip_properties(t);
241
242 do {
243 while (fdt_next_subnode(t, &name)) {
244 fdt_skip_properties(t);
245 pending++;
246 }
247
Andrew Scull7364a8e2018-07-19 15:39:29 +0100248 if (!fdt_tokenizer_token(t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100249 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100250 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100251
252 if (token != FDT_END_NODE) {
253 t->cur = t->end;
254 return false;
255 }
256
257 pending--;
258 } while (pending);
259
260 return true;
261}
262
263bool fdt_read_property(const struct fdt_node *node, const char *name,
264 const char **buf, uint32_t *size)
265{
266 struct fdt_tokenizer t;
267 const char *prop_name;
268
269 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
270
271 while (fdt_next_property(&t, &prop_name, buf, size)) {
Andrew Scull7364a8e2018-07-19 15:39:29 +0100272 if (!strcmp(prop_name, name)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100273 return true;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100274 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100275 }
276
277 return false;
278}
279
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100280bool fdt_first_child(struct fdt_node *node, const char **child_name)
281{
282 struct fdt_tokenizer t;
283
284 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
285
286 fdt_skip_properties(&t);
287
Andrew Scull7364a8e2018-07-19 15:39:29 +0100288 if (!fdt_next_subnode(&t, child_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100289 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100290 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100291
292 node->begin = t.cur;
293
294 return true;
295}
296
297bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name)
298{
299 struct fdt_tokenizer t;
300
301 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
302
Andrew Scull7364a8e2018-07-19 15:39:29 +0100303 if (!fdt_skip_node(&t)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100304 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100305 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100306
Andrew Scull7364a8e2018-07-19 15:39:29 +0100307 if (!fdt_next_subnode(&t, sibling_name)) {
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100308 return false;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100309 }
Wedson Almeida Filho87009642018-07-02 10:20:07 +0100310
311 node->begin = t.cur;
312
313 return true;
314}
315
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100316bool fdt_find_child(struct fdt_node *node, const char *child)
317{
318 struct fdt_tokenizer t;
319 const char *name;
320
321 fdt_tokenizer_init(&t, node->strs, node->begin, node->end);
322
323 fdt_skip_properties(&t);
324
325 while (fdt_next_subnode(&t, &name)) {
326 if (!strcmp(name, child)) {
327 node->begin = t.cur;
328 return true;
329 }
330
331 fdt_skip_node(&t);
332 }
333
334 return false;
335}
336
337void fdt_dump(struct fdt_header *hdr)
338{
339 uint32_t token;
340 size_t depth = 0;
341 const char *name;
342 struct fdt_tokenizer t;
343 struct fdt_node node;
344
345 /* Traverse the whole thing. */
Andrew Scull83644292018-10-05 22:38:46 +0100346 if (!fdt_root_node(&node, hdr)) {
347 dlog("FDT failed validation.\n");
348 return;
349 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100350
351 fdt_tokenizer_init(&t, node.strs, node.begin, node.end);
352
353 do {
354 while (fdt_next_subnode(&t, &name)) {
355 const char *buf;
356 uint32_t size;
357
358 dlog("%*sNew node: \"%s\"\n", 2 * depth, "", name);
359 depth++;
360 while (fdt_next_property(&t, &name, &buf, &size)) {
Andrew Scull37402872018-10-24 14:23:06 +0100361 uint32_t i;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000362
Andrew Scull4f170f52018-07-19 12:58:20 +0100363 dlog("%*sproperty: \"%s\" (", 2 * depth, "",
364 name);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100365 for (i = 0; i < size; i++) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100366 dlog("%s%02x", i == 0 ? "" : " ",
367 buf[i]);
Andrew Scull7364a8e2018-07-19 15:39:29 +0100368 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100369 dlog(")\n");
370 }
371 }
372
Andrew Scull7364a8e2018-07-19 15:39:29 +0100373 if (!fdt_tokenizer_token(&t, &token)) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100374 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100375 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100376
Andrew Scull7364a8e2018-07-19 15:39:29 +0100377 if (token != FDT_END_NODE) {
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100378 return;
Andrew Scull7364a8e2018-07-19 15:39:29 +0100379 }
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100380
381 depth--;
382 } while (depth);
383
Andrew Scull29d40392018-07-16 11:46:59 +0100384 dlog("fdt: off_mem_rsvmap=%u\n", be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100385 {
Andrew Scull4f170f52018-07-19 12:58:20 +0100386 struct fdt_reserve_entry *e =
387 (struct fdt_reserve_entry
Andrew Scull37402872018-10-24 14:23:06 +0100388 *)((uintptr_t)hdr +
389 be32toh(hdr->off_mem_rsvmap));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100390 while (e->address || e->size) {
Andrew Scull4f170f52018-07-19 12:58:20 +0100391 dlog("Entry: %p (0x%x bytes)\n", be64toh(e->address),
392 be64toh(e->size));
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100393 e++;
394 }
395 }
396}
397
Andrew Scull37402872018-10-24 14:23:06 +0100398void fdt_add_mem_reservation(struct fdt_header *hdr, uint64_t addr,
399 uint64_t len)
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100400{
401 /* TODO: Clean this up. */
Andrew Scull37402872018-10-24 14:23:06 +0100402 uint8_t *begin = (uint8_t *)hdr + be32toh(hdr->off_mem_rsvmap);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100403 struct fdt_reserve_entry *e = (struct fdt_reserve_entry *)begin;
Andrew Scullcbefbdb2019-01-11 16:36:26 +0000404
Andrew Scull4f170f52018-07-19 12:58:20 +0100405 hdr->totalsize = htobe32(be32toh(hdr->totalsize) +
406 sizeof(struct fdt_reserve_entry));
407 hdr->off_dt_struct = htobe32(be32toh(hdr->off_dt_struct) +
408 sizeof(struct fdt_reserve_entry));
409 hdr->off_dt_strings = htobe32(be32toh(hdr->off_dt_strings) +
410 sizeof(struct fdt_reserve_entry));
411 memmove(begin + sizeof(struct fdt_reserve_entry), begin,
412 be32toh(hdr->totalsize) - be32toh(hdr->off_mem_rsvmap));
Andrew Scull29d40392018-07-16 11:46:59 +0100413 e->address = htobe64(addr);
414 e->size = htobe64(len);
Wedson Almeida Filho987c0ff2018-06-20 16:34:38 +0100415}
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100416
417size_t fdt_header_size(void)
418{
419 return sizeof(struct fdt_header);
420}
421
Andrew Scull37402872018-10-24 14:23:06 +0100422uint32_t fdt_total_size(struct fdt_header *hdr)
Wedson Almeida Filhofed69022018-07-11 15:39:12 +0100423{
424 return be32toh(hdr->totalsize);
425}