blob: 8121790c944f459a99a99dbc12b0ca9ce51b85fe [file] [log] [blame]
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01001#include "fdt_handler.h"
2
3#include "boot_params.h"
4#include "dlog.h"
5#include "fdt.h"
6#include "mm.h"
7#include "std.h"
8
9static uint64_t convert_number(const char *data, uint32_t size)
10{
11 union {
12 volatile uint64_t v;
13 char a[8];
14 } t;
15
16 switch (size) {
17 case sizeof(uint32_t):
18 return be32toh(*(uint32_t *)data);
19 case sizeof(uint64_t):
20 memcpy(t.a, data, sizeof(uint64_t));
21 return be64toh(t.v);
22 default:
23 return 0;
24 }
25}
26
27static bool fdt_read_number(const struct fdt_node *node, const char *name,
28 uint64_t *value)
29{
30 const char *data;
31 uint32_t size;
32
33 if (!fdt_read_property(node, name, &data, &size)) {
34 return false;
35 }
36
37 switch (size) {
38 case sizeof(uint32_t):
39 case sizeof(uint64_t):
40 *value = convert_number(data, size);
41 break;
42
43 default:
44 return false;
45 }
46
47 return true;
48}
49
50static bool fdt_write_number(struct fdt_node *node, const char *name,
51 uint64_t value)
52{
53 const char *data;
54 uint32_t size;
55 union {
56 volatile uint64_t v;
57 char a[8];
58 } t;
59
60 if (!fdt_read_property(node, name, &data, &size)) {
61 return false;
62 }
63
64 switch (size) {
65 case sizeof(uint32_t):
66 *(uint32_t *)data = be32toh(value);
67 break;
68
69 case sizeof(uint64_t):
70 t.v = be64toh(value);
71 memcpy((void *)data, t.a, sizeof(uint64_t));
72 break;
73
74 default:
75 return false;
76 }
77
78 return true;
79}
80
81/**
82 * Finds the memory region where initrd is stored, and udpates the fdt node
83 * cursor to the node called "chosen".
84 */
85static bool find_initrd(struct fdt_node *n, struct boot_params *p)
86{
Andrew Scull265ada92018-07-30 15:19:01 +010087 uint64_t begin;
88 uint64_t end;
89
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010090 if (!fdt_find_child(n, "chosen")) {
91 dlog("Unable to find 'chosen'\n");
92 return false;
93 }
94
Andrew Scull265ada92018-07-30 15:19:01 +010095 if (!fdt_read_number(n, "linux,initrd-start", &begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010096 dlog("Unable to read linux,initrd-start\n");
97 return false;
98 }
99
Andrew Scull265ada92018-07-30 15:19:01 +0100100 if (!fdt_read_number(n, "linux,initrd-end", &end)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100101 dlog("Unable to read linux,initrd-end\n");
102 return false;
103 }
104
Andrew Scull265ada92018-07-30 15:19:01 +0100105 p->initrd_begin = pa_init(begin);
106 p->initrd_end = pa_init(end);
107
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100108 return true;
109}
110
111static void find_memory_range(const struct fdt_node *root,
112 struct boot_params *p)
113{
114 struct fdt_node n = *root;
115 const char *name;
116 uint64_t address_size;
117 uint64_t size_size;
118 uint64_t entry_size;
119
120 /* Get the sizes of memory range addresses and sizes. */
121 if (fdt_read_number(&n, "#address-cells", &address_size)) {
122 address_size *= sizeof(uint32_t);
123 } else {
124 address_size = sizeof(uint32_t);
125 }
126
127 if (fdt_read_number(&n, "#size-cells", &size_size)) {
128 size_size *= sizeof(uint32_t);
129 } else {
130 size_size = sizeof(uint32_t);
131 }
132
133 entry_size = address_size + size_size;
134
135 /* Look for nodes with the device_type set to "memory". */
136 if (!fdt_first_child(&n, &name)) {
137 return;
138 }
139
140 do {
141 const char *data;
142 uint32_t size;
143 if (!fdt_read_property(&n, "device_type", &data, &size) ||
144 size != sizeof("memory") ||
145 memcmp(data, "memory", sizeof("memory")) != 0 ||
146 !fdt_read_property(&n, "reg", &data, &size)) {
147 continue;
148 }
149
150 /* Traverse all memory ranges within this node. */
151 while (size >= entry_size) {
Andrew Scull265ada92018-07-30 15:19:01 +0100152 uintpaddr_t addr = convert_number(data, address_size);
153 size_t len =
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100154 convert_number(data + address_size, size_size);
155
Andrew Scull265ada92018-07-30 15:19:01 +0100156 if (len > pa_addr(p->mem_end) - pa_addr(p->mem_begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100157 /* Remember the largest range we've found. */
Andrew Scull265ada92018-07-30 15:19:01 +0100158 p->mem_begin = pa_init(addr);
159 p->mem_end = pa_init(addr + len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100160 }
161
162 size -= entry_size;
163 data += entry_size;
164 }
165 } while (fdt_next_sibling(&n, &name));
166
167 /* TODO: Check for "reserved-memory" nodes. */
168}
169
Andrew Scull265ada92018-07-30 15:19:01 +0100170bool fdt_get_boot_params(paddr_t fdt_addr, struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100171{
Andrew Scull265ada92018-07-30 15:19:01 +0100172 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100173 struct fdt_node n;
174 bool ret = false;
175
176 /* Map the fdt header in. */
Andrew Scull265ada92018-07-30 15:19:01 +0100177 if (!mm_identity_map(mm_va_from_pa(fdt_addr),
178 va_init(pa_addr(fdt_addr) + fdt_header_size()),
Andrew Scullfe636b12018-07-30 14:15:54 +0100179 MM_MODE_R)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100180 dlog("Unable to map FDT header.\n");
181 goto err_unmap_fdt_header;
182 }
183
Andrew Scull265ada92018-07-30 15:19:01 +0100184 fdt = mm_ptr_from_va(mm_va_from_pa(fdt_addr));
185
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100186 if (!fdt_root_node(&n, fdt)) {
187 dlog("FDT failed validation.\n");
188 goto err_unmap_fdt_header;
189 }
190
191 /* Map the rest of the fdt in. */
Andrew Scull265ada92018-07-30 15:19:01 +0100192 if (!mm_identity_map(mm_va_from_pa(fdt_addr),
193 va_init(pa_addr(fdt_addr) + fdt_total_size(fdt)),
Andrew Scullfe636b12018-07-30 14:15:54 +0100194 MM_MODE_R)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100195 dlog("Unable to map full FDT.\n");
196 goto err_unmap_fdt_header;
197 }
198
199 if (!fdt_find_child(&n, "")) {
200 dlog("Unable to find FDT root node.\n");
201 goto out_unmap_fdt;
202 }
203
Andrew Scull265ada92018-07-30 15:19:01 +0100204 p->mem_begin = pa_init(0);
205 p->mem_end = pa_init(0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100206 find_memory_range(&n, p);
207
208 if (!find_initrd(&n, p)) {
209 goto out_unmap_fdt;
210 }
211
212 p->kernel_arg = (size_t)fdt;
213 ret = true;
214
215out_unmap_fdt:
Andrew Scull265ada92018-07-30 15:19:01 +0100216 mm_unmap(mm_va_from_pa(fdt_addr),
217 va_init(pa_addr(fdt_addr) + fdt_total_size(fdt)), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100218 return ret;
219
220err_unmap_fdt_header:
Andrew Scull265ada92018-07-30 15:19:01 +0100221 mm_unmap(mm_va_from_pa(fdt_addr),
222 va_init(pa_addr(fdt_addr) + fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100223 return false;
224}
225
Andrew Scull265ada92018-07-30 15:19:01 +0100226bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100227{
Andrew Scull265ada92018-07-30 15:19:01 +0100228 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100229 struct fdt_node n;
230 bool ret = false;
231
232 /* Map the fdt header in. */
Andrew Scull265ada92018-07-30 15:19:01 +0100233 if (!mm_identity_map(mm_va_from_pa(fdt_addr),
234 va_init(pa_addr(fdt_addr) + fdt_header_size()),
Andrew Scullfe636b12018-07-30 14:15:54 +0100235 MM_MODE_R)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100236 dlog("Unable to map FDT header.\n");
237 return false;
238 }
239
Andrew Scull265ada92018-07-30 15:19:01 +0100240 fdt = mm_ptr_from_va(mm_va_from_pa(fdt_addr));
241
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100242 if (!fdt_root_node(&n, fdt)) {
243 dlog("FDT failed validation.\n");
244 goto err_unmap_fdt_header;
245 }
246
247 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull265ada92018-07-30 15:19:01 +0100248 if (!mm_identity_map(mm_va_from_pa(fdt_addr),
249 va_init(pa_addr(fdt_addr) + fdt_total_size(fdt) +
250 PAGE_SIZE),
Andrew Scullfe636b12018-07-30 14:15:54 +0100251 MM_MODE_R | MM_MODE_W)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100252 dlog("Unable to map FDT in r/w mode.\n");
253 goto err_unmap_fdt_header;
254 }
255
256 if (!fdt_find_child(&n, "")) {
257 dlog("Unable to find FDT root node.\n");
258 goto out_unmap_fdt;
259 }
260
261 if (!fdt_find_child(&n, "chosen")) {
262 dlog("Unable to find 'chosen'\n");
263 goto out_unmap_fdt;
264 }
265
266 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100267 if (!fdt_write_number(&n, "linux,initrd-start",
268 pa_addr(p->initrd_begin))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100269 dlog("Unable to write linux,initrd-start\n");
270 goto out_unmap_fdt;
271 }
272
Andrew Scull265ada92018-07-30 15:19:01 +0100273 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100274 dlog("Unable to write linux,initrd-end\n");
275 goto out_unmap_fdt;
276 }
277
278 /* Patch fdt to reserve primary VM memory. */
279 {
280 size_t tmp = (size_t)&plat_update_boot_params;
281 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
282 fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
283 }
284
285 /* Patch fdt to reserve memory for secondary VMs. */
Andrew Scull265ada92018-07-30 15:19:01 +0100286 fdt_add_mem_reservation(
287 fdt, pa_addr(p->reserved_begin),
288 pa_addr(p->reserved_end) - pa_addr(p->reserved_begin));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100289
290 ret = true;
291
292out_unmap_fdt:
293 /* Unmap FDT. */
Andrew Scull265ada92018-07-30 15:19:01 +0100294 if (!mm_unmap(mm_va_from_pa(fdt_addr),
295 va_init(pa_addr(fdt_addr) + fdt_total_size(fdt) +
296 PAGE_SIZE),
297 0)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100298 dlog("Unable to unmap writable FDT.\n");
299 return false;
300 }
301 return ret;
302
303err_unmap_fdt_header:
Andrew Scull265ada92018-07-30 15:19:01 +0100304 mm_unmap(mm_va_from_pa(fdt_addr),
305 va_init(pa_addr(fdt_addr) + fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100306 return false;
307}