blob: 91a17d8675aa8b6b5d367d835385a947b46fc436 [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
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100111static void find_memory_ranges(const struct fdt_node *root,
112 struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100113{
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;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100119 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100120
121 /* Get the sizes of memory range addresses and sizes. */
122 if (fdt_read_number(&n, "#address-cells", &address_size)) {
123 address_size *= sizeof(uint32_t);
124 } else {
125 address_size = sizeof(uint32_t);
126 }
127
128 if (fdt_read_number(&n, "#size-cells", &size_size)) {
129 size_size *= sizeof(uint32_t);
130 } else {
131 size_size = sizeof(uint32_t);
132 }
133
134 entry_size = address_size + size_size;
135
136 /* Look for nodes with the device_type set to "memory". */
137 if (!fdt_first_child(&n, &name)) {
138 return;
139 }
140
141 do {
142 const char *data;
143 uint32_t size;
144 if (!fdt_read_property(&n, "device_type", &data, &size) ||
145 size != sizeof("memory") ||
146 memcmp(data, "memory", sizeof("memory")) != 0 ||
147 !fdt_read_property(&n, "reg", &data, &size)) {
148 continue;
149 }
150
151 /* Traverse all memory ranges within this node. */
152 while (size >= entry_size) {
Andrew Scull265ada92018-07-30 15:19:01 +0100153 uintpaddr_t addr = convert_number(data, address_size);
154 size_t len =
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100155 convert_number(data + address_size, size_size);
156
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100157 if (mem_range_index < MAX_MEM_RANGES) {
158 p->mem_ranges[mem_range_index].begin =
159 pa_init(addr);
160 p->mem_ranges[mem_range_index].end =
161 pa_init(addr + len);
162 ++mem_range_index;
163 } else {
164 dlog("Found memory range %u in FDT but only "
165 "%u supported, ignoring additional range "
166 "of size %u.\n",
167 mem_range_index, MAX_MEM_RANGES, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100168 }
169
170 size -= entry_size;
171 data += entry_size;
172 }
173 } while (fdt_next_sibling(&n, &name));
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100174 p->mem_ranges_count = mem_range_index;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100175
176 /* TODO: Check for "reserved-memory" nodes. */
177}
178
Andrew Scull265ada92018-07-30 15:19:01 +0100179bool fdt_get_boot_params(paddr_t fdt_addr, struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100180{
Andrew Scull265ada92018-07-30 15:19:01 +0100181 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100182 struct fdt_node n;
183 bool ret = false;
184
185 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100186 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
187 MM_MODE_R);
188 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100189 dlog("Unable to map FDT header.\n");
190 goto err_unmap_fdt_header;
191 }
192
193 if (!fdt_root_node(&n, fdt)) {
194 dlog("FDT failed validation.\n");
195 goto err_unmap_fdt_header;
196 }
197
198 /* Map the rest of the fdt in. */
Andrew Scull80871322018-08-06 12:04:09 +0100199 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)),
200 MM_MODE_R);
201 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100202 dlog("Unable to map full FDT.\n");
203 goto err_unmap_fdt_header;
204 }
205
206 if (!fdt_find_child(&n, "")) {
207 dlog("Unable to find FDT root node.\n");
208 goto out_unmap_fdt;
209 }
210
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100211 p->mem_ranges_count = 0;
212 find_memory_ranges(&n, p);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100213
214 if (!find_initrd(&n, p)) {
215 goto out_unmap_fdt;
216 }
217
218 p->kernel_arg = (size_t)fdt;
219 ret = true;
220
221out_unmap_fdt:
Andrew Scull80871322018-08-06 12:04:09 +0100222 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100223 return ret;
224
225err_unmap_fdt_header:
Andrew Scull80871322018-08-06 12:04:09 +0100226 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100227 return false;
228}
229
Andrew Scull265ada92018-07-30 15:19:01 +0100230bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100231{
Andrew Scull265ada92018-07-30 15:19:01 +0100232 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100233 struct fdt_node n;
234 bool ret = false;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100235 size_t i;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100236
237 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100238 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
239 MM_MODE_R);
240 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100241 dlog("Unable to map FDT header.\n");
242 return false;
243 }
244
245 if (!fdt_root_node(&n, fdt)) {
246 dlog("FDT failed validation.\n");
247 goto err_unmap_fdt_header;
248 }
249
250 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull80871322018-08-06 12:04:09 +0100251 fdt = mm_identity_map(fdt_addr,
252 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
253 MM_MODE_R | MM_MODE_W);
254 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100255 dlog("Unable to map FDT in r/w mode.\n");
256 goto err_unmap_fdt_header;
257 }
258
259 if (!fdt_find_child(&n, "")) {
260 dlog("Unable to find FDT root node.\n");
261 goto out_unmap_fdt;
262 }
263
264 if (!fdt_find_child(&n, "chosen")) {
265 dlog("Unable to find 'chosen'\n");
266 goto out_unmap_fdt;
267 }
268
269 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100270 if (!fdt_write_number(&n, "linux,initrd-start",
271 pa_addr(p->initrd_begin))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100272 dlog("Unable to write linux,initrd-start\n");
273 goto out_unmap_fdt;
274 }
275
Andrew Scull265ada92018-07-30 15:19:01 +0100276 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100277 dlog("Unable to write linux,initrd-end\n");
278 goto out_unmap_fdt;
279 }
280
281 /* Patch fdt to reserve primary VM memory. */
282 {
283 size_t tmp = (size_t)&plat_update_boot_params;
284 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
285 fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
286 }
287
288 /* Patch fdt to reserve memory for secondary VMs. */
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100289 for (i = 0; i < p->reserved_ranges_count; ++i) {
290 fdt_add_mem_reservation(
291 fdt, pa_addr(p->reserved_ranges[i].begin),
292 pa_addr(p->reserved_ranges[i].end) -
293 pa_addr(p->reserved_ranges[i].begin));
294 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100295
296 ret = true;
297
298out_unmap_fdt:
299 /* Unmap FDT. */
Andrew Scull80871322018-08-06 12:04:09 +0100300 if (!mm_unmap(fdt_addr,
301 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE), 0)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100302 dlog("Unable to unmap writable FDT.\n");
303 return false;
304 }
305 return ret;
306
307err_unmap_fdt_header:
Andrew Scull80871322018-08-06 12:04:09 +0100308 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100309 return false;
310}