1/* Copyright (c) 2010-2017, The Regents of the University of California
2 * (Regents).  All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 * 1. Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * 2. Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * 3. Neither the name of the Regents nor the
12 * names of its contributors may be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
16 * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
17 * OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
18 * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19 *
20 * REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
23 * HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
24 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 */
26
27/* This file is copied from RISC-V tools. It's modified to work with seL4 */
28
29#include <stdint.h>
30#include <string.h>
31#include <stdint.h>
32#include <string.h>
33#include <plat/machine/fdt.h>
34#include <config.h>
35#include <util.h>
36
37#define FDT_MAGIC   0xd00dfeed
38#define FDT_VERSION 17
39
40#define FDT_BEGIN_NODE  1
41#define FDT_END_NODE    2
42#define FDT_PROP    3
43#define FDT_NOP     4
44#define FDT_END     9
45
46struct fdt_header {
47    uint32_t magic;
48    uint32_t totalsize;
49    uint32_t off_dt_struct;
50    uint32_t off_dt_strings;
51    uint32_t off_mem_rsvmap;
52    uint32_t version;
53    uint32_t last_comp_version; /* <= 17 */
54    uint32_t boot_cpuid_phys;
55    uint32_t size_dt_strings;
56    uint32_t size_dt_struct;
57};
58
59struct fdt_scan_node {
60    const struct fdt_scan_node *parent;
61    const char *name;
62    int address_cells;
63    int size_cells;
64};
65
66struct fdt_scan_prop {
67    const struct fdt_scan_node *node;
68    const char *name;
69    uint32_t *value;
70    int len; // in bytes of value
71};
72
73/* workaround because string literals are not supported by the C parser */
74const char fdt_address_cells[] = {'#', 'a', 'd', 'd', 'r', 'e', 's', 's', '-', 'c', 'e', 'l', 'l', 's', 0};
75const char fdt_size_cells[] = {'#', 's', 'i', 'z', 'e', '-', 'c', 'e', 'l', 'l', 's', 0};
76const char fdt_reg[] = {'r', 'e', 'g', 0};
77const char fdt_device_type[] = {'d', 'e', 'v', 'i', 'c', 'e', '_', 't', 'y', 'p', 'e', 0};
78const char fdt_memory[] = {'m', 'e', 'm', 'o', 'r', 'y', 0};
79
80static inline uint32_t bswap(uint32_t x)
81{
82    uint32_t y = (x & 0x00FF00FF) <<  8 | (x & 0xFF00FF00) >>  8;
83    uint32_t z = (y & 0x0000FFFF) << 16 | (y & 0xFFFF0000) >> 16;
84    return z;
85}
86
87struct scan_state {
88    int found_memory;
89    const uint32_t *reg_value;
90    int reg_len;
91};
92
93static const uint32_t *fdt_get_address(const struct fdt_scan_node *node, const uint32_t *value, uint64_t *result)
94{
95    *result = 0;
96    for (int cells = node->address_cells; cells > 0; --cells) {
97        *result = (*result << 32) + bswap(*value++);
98    }
99    return value;
100}
101
102static const uint32_t *fdt_get_size(const struct fdt_scan_node *node, const uint32_t *value, uint64_t *result)
103{
104    *result = 0;
105    for (int cells = node->size_cells; cells > 0; --cells) {
106        *result = (*result << 32) + bswap(*value++);
107    }
108    return value;
109}
110
111static uint32_t *fdt_scan_helper(
112    uint32_t *lex,
113    const char *strings,
114    struct fdt_scan_node *node,
115    struct scan_state *state)
116{
117    struct fdt_scan_node child;
118    struct fdt_scan_prop prop;
119    int UNUSED last = 0;
120
121    child.parent = node;
122    // these are the default cell counts, as per the FDT spec
123    child.address_cells = 2;
124    child.size_cells = 1;
125    prop.node = node;
126
127    while (1) {
128        switch (bswap(lex[0])) {
129        case FDT_NOP: {
130            lex += 1;
131            break;
132        }
133        case FDT_PROP: {
134            assert (!last);
135            prop.name  = strings + bswap(lex[2]);
136            prop.len   = bswap(lex[1]);
137            prop.value = lex + 3;
138            if (node && !strncmp(prop.name, fdt_address_cells, 14)) {
139                node->address_cells = bswap(lex[3]);
140            }
141            if (node && !strncmp(prop.name, fdt_size_cells, 11))    {
142                node->size_cells    = bswap(lex[3]);
143            }
144            lex += 3 + (prop.len + 3) / 4;
145            if (state->found_memory && strncmp(prop.name, fdt_reg, 3) == 0) {
146                state->reg_value = prop.value;
147                state->reg_len = prop.len;
148            }
149            if (strncmp(prop.name, fdt_device_type, 11) == 0 && strncmp((const char*)prop.value, fdt_memory, 6) == 0) {
150                state->found_memory = 1;
151            }
152            break;
153        }
154        case FDT_BEGIN_NODE: {
155            uint32_t *lex_next;
156            last = 1;
157            child.name = (const char *)(lex + 1);
158            lex_next = fdt_scan_helper(
159                           lex + 2 + strnlen(child.name, 1024) / 4,
160                           strings, &child, state);
161            lex = lex_next;
162            break;
163        }
164        case FDT_END_NODE: {
165            if (state->found_memory) {
166                const uint32_t *value = state->reg_value;
167                const uint32_t *end = value + state->reg_len / 4;
168
169                assert (state->reg_value && state->reg_len % 4 == 0);
170
171                while (end - value > 0) {
172                    uint64_t base, size;
173                    value = fdt_get_address(node->parent, value, &base);
174                    value = fdt_get_size   (node->parent, value, &size);
175                    if (!add_avail_p_reg((p_region_t) {
176                    base, base + size
177                })) {
178                        printf("Failed to add physical memory region %llu-%llu\n", (unsigned long long)base, (unsigned long long)(base + size));
179                    }
180                }
181                state->found_memory = 0;
182            }
183            return lex + 1;
184        }
185        default: { // FDT_END
186            return lex;
187        }
188        }
189    }
190}
191
192void parseFDT(void *fdt)
193{
194    struct fdt_header *header = (struct fdt_header *)fdt;
195
196    // Only process FDT that we understand
197    if (bswap(header->magic) != FDT_MAGIC ||
198            bswap(header->last_comp_version) > FDT_VERSION) {
199        return;
200    }
201
202    const char *strings = (const char *)((word_t)fdt + bswap(header->off_dt_strings));
203    uint32_t *lex = (uint32_t *)((word_t)fdt + bswap(header->off_dt_struct));
204
205    struct scan_state state;
206    state.found_memory = 0;
207
208    fdt_scan_helper(lex, strings, 0, &state);
209}
210
211uint32_t fdt_size(void *fdt)
212{
213    struct fdt_header *header = (struct fdt_header *)fdt;
214
215    // Only process FDT that we understand
216    if (bswap(header->magic) != FDT_MAGIC ||
217            bswap(header->last_comp_version) > FDT_VERSION) {
218        return 0;
219    }
220    return bswap(header->totalsize);
221}
222