blob: 4a93e66467ba435b4719ffcecdad9b831db9bcc4 [file] [log] [blame]
Andrew Scull18c78fc2018-08-20 12:57:41 +01001#include "hf/fdt_handler.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01002
Andrew Scull18c78fc2018-08-20 12:57:41 +01003#include "hf/boot_params.h"
4#include "hf/dlog.h"
5#include "hf/fdt.h"
6#include "hf/mm.h"
7#include "hf/std.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01008
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/**
Andrew Walbranfb88f2c2018-09-18 15:23:49 +010082 * Finds the memory region where initrd is stored, and updates the fdt node
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010083 * 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 Scull80871322018-08-06 12:04:09 +0100177 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
178 MM_MODE_R);
179 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100180 dlog("Unable to map FDT header.\n");
181 goto err_unmap_fdt_header;
182 }
183
184 if (!fdt_root_node(&n, fdt)) {
185 dlog("FDT failed validation.\n");
186 goto err_unmap_fdt_header;
187 }
188
189 /* Map the rest of the fdt in. */
Andrew Scull80871322018-08-06 12:04:09 +0100190 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)),
191 MM_MODE_R);
192 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100193 dlog("Unable to map full FDT.\n");
194 goto err_unmap_fdt_header;
195 }
196
197 if (!fdt_find_child(&n, "")) {
198 dlog("Unable to find FDT root node.\n");
199 goto out_unmap_fdt;
200 }
201
Andrew Scull265ada92018-07-30 15:19:01 +0100202 p->mem_begin = pa_init(0);
203 p->mem_end = pa_init(0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100204 find_memory_range(&n, p);
205
206 if (!find_initrd(&n, p)) {
207 goto out_unmap_fdt;
208 }
209
210 p->kernel_arg = (size_t)fdt;
211 ret = true;
212
213out_unmap_fdt:
Andrew Scull80871322018-08-06 12:04:09 +0100214 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100215 return ret;
216
217err_unmap_fdt_header:
Andrew Scull80871322018-08-06 12:04:09 +0100218 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100219 return false;
220}
221
Andrew Scull265ada92018-07-30 15:19:01 +0100222bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100223{
Andrew Scull265ada92018-07-30 15:19:01 +0100224 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100225 struct fdt_node n;
226 bool ret = false;
227
228 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100229 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
230 MM_MODE_R);
231 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100232 dlog("Unable to map FDT header.\n");
233 return false;
234 }
235
236 if (!fdt_root_node(&n, fdt)) {
237 dlog("FDT failed validation.\n");
238 goto err_unmap_fdt_header;
239 }
240
241 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull80871322018-08-06 12:04:09 +0100242 fdt = mm_identity_map(fdt_addr,
243 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
244 MM_MODE_R | MM_MODE_W);
245 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100246 dlog("Unable to map FDT in r/w mode.\n");
247 goto err_unmap_fdt_header;
248 }
249
250 if (!fdt_find_child(&n, "")) {
251 dlog("Unable to find FDT root node.\n");
252 goto out_unmap_fdt;
253 }
254
255 if (!fdt_find_child(&n, "chosen")) {
256 dlog("Unable to find 'chosen'\n");
257 goto out_unmap_fdt;
258 }
259
260 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100261 if (!fdt_write_number(&n, "linux,initrd-start",
262 pa_addr(p->initrd_begin))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100263 dlog("Unable to write linux,initrd-start\n");
264 goto out_unmap_fdt;
265 }
266
Andrew Scull265ada92018-07-30 15:19:01 +0100267 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100268 dlog("Unable to write linux,initrd-end\n");
269 goto out_unmap_fdt;
270 }
271
272 /* Patch fdt to reserve primary VM memory. */
273 {
274 size_t tmp = (size_t)&plat_update_boot_params;
275 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
276 fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
277 }
278
279 /* Patch fdt to reserve memory for secondary VMs. */
Andrew Scull265ada92018-07-30 15:19:01 +0100280 fdt_add_mem_reservation(
281 fdt, pa_addr(p->reserved_begin),
282 pa_addr(p->reserved_end) - pa_addr(p->reserved_begin));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100283
284 ret = true;
285
286out_unmap_fdt:
287 /* Unmap FDT. */
Andrew Scull80871322018-08-06 12:04:09 +0100288 if (!mm_unmap(fdt_addr,
289 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE), 0)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100290 dlog("Unable to unmap writable FDT.\n");
291 return false;
292 }
293 return ret;
294
295err_unmap_fdt_header:
Andrew Scull80871322018-08-06 12:04:09 +0100296 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100297 return false;
298}