blob: cb910ac8f85f1075cc04816568c3abd176cf60c9 [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"
Andrew Scull5991ec92018-10-08 14:55:02 +01006#include "hf/layout.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +01007#include "hf/mm.h"
8#include "hf/std.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +01009
10static uint64_t convert_number(const char *data, uint32_t size)
11{
12 union {
13 volatile uint64_t v;
14 char a[8];
15 } t;
16
17 switch (size) {
18 case sizeof(uint32_t):
19 return be32toh(*(uint32_t *)data);
20 case sizeof(uint64_t):
21 memcpy(t.a, data, sizeof(uint64_t));
22 return be64toh(t.v);
23 default:
24 return 0;
25 }
26}
27
28static bool fdt_read_number(const struct fdt_node *node, const char *name,
29 uint64_t *value)
30{
31 const char *data;
32 uint32_t size;
33
34 if (!fdt_read_property(node, name, &data, &size)) {
35 return false;
36 }
37
38 switch (size) {
39 case sizeof(uint32_t):
40 case sizeof(uint64_t):
41 *value = convert_number(data, size);
42 break;
43
44 default:
45 return false;
46 }
47
48 return true;
49}
50
51static bool fdt_write_number(struct fdt_node *node, const char *name,
52 uint64_t value)
53{
54 const char *data;
55 uint32_t size;
56 union {
57 volatile uint64_t v;
58 char a[8];
59 } t;
60
61 if (!fdt_read_property(node, name, &data, &size)) {
62 return false;
63 }
64
65 switch (size) {
66 case sizeof(uint32_t):
67 *(uint32_t *)data = be32toh(value);
68 break;
69
70 case sizeof(uint64_t):
71 t.v = be64toh(value);
72 memcpy((void *)data, t.a, sizeof(uint64_t));
73 break;
74
75 default:
76 return false;
77 }
78
79 return true;
80}
81
82/**
Andrew Walbranfb88f2c2018-09-18 15:23:49 +010083 * Finds the memory region where initrd is stored, and updates the fdt node
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010084 * cursor to the node called "chosen".
85 */
86static bool find_initrd(struct fdt_node *n, struct boot_params *p)
87{
Andrew Scull265ada92018-07-30 15:19:01 +010088 uint64_t begin;
89 uint64_t end;
90
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010091 if (!fdt_find_child(n, "chosen")) {
92 dlog("Unable to find 'chosen'\n");
93 return false;
94 }
95
Andrew Scull265ada92018-07-30 15:19:01 +010096 if (!fdt_read_number(n, "linux,initrd-start", &begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010097 dlog("Unable to read linux,initrd-start\n");
98 return false;
99 }
100
Andrew Scull265ada92018-07-30 15:19:01 +0100101 if (!fdt_read_number(n, "linux,initrd-end", &end)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100102 dlog("Unable to read linux,initrd-end\n");
103 return false;
104 }
105
Andrew Scull265ada92018-07-30 15:19:01 +0100106 p->initrd_begin = pa_init(begin);
107 p->initrd_end = pa_init(end);
108
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100109 return true;
110}
111
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100112static void find_memory_ranges(const struct fdt_node *root,
113 struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100114{
115 struct fdt_node n = *root;
116 const char *name;
117 uint64_t address_size;
118 uint64_t size_size;
119 uint64_t entry_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100120 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100121
122 /* Get the sizes of memory range addresses and sizes. */
123 if (fdt_read_number(&n, "#address-cells", &address_size)) {
124 address_size *= sizeof(uint32_t);
125 } else {
126 address_size = sizeof(uint32_t);
127 }
128
129 if (fdt_read_number(&n, "#size-cells", &size_size)) {
130 size_size *= sizeof(uint32_t);
131 } else {
132 size_size = sizeof(uint32_t);
133 }
134
135 entry_size = address_size + size_size;
136
137 /* Look for nodes with the device_type set to "memory". */
138 if (!fdt_first_child(&n, &name)) {
139 return;
140 }
141
142 do {
143 const char *data;
144 uint32_t size;
145 if (!fdt_read_property(&n, "device_type", &data, &size) ||
146 size != sizeof("memory") ||
147 memcmp(data, "memory", sizeof("memory")) != 0 ||
148 !fdt_read_property(&n, "reg", &data, &size)) {
149 continue;
150 }
151
152 /* Traverse all memory ranges within this node. */
153 while (size >= entry_size) {
Andrew Scull265ada92018-07-30 15:19:01 +0100154 uintpaddr_t addr = convert_number(data, address_size);
155 size_t len =
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100156 convert_number(data + address_size, size_size);
157
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100158 if (mem_range_index < MAX_MEM_RANGES) {
159 p->mem_ranges[mem_range_index].begin =
160 pa_init(addr);
161 p->mem_ranges[mem_range_index].end =
162 pa_init(addr + len);
163 ++mem_range_index;
164 } else {
165 dlog("Found memory range %u in FDT but only "
166 "%u supported, ignoring additional range "
167 "of size %u.\n",
168 mem_range_index, MAX_MEM_RANGES, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100169 }
170
171 size -= entry_size;
172 data += entry_size;
173 }
174 } while (fdt_next_sibling(&n, &name));
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100175 p->mem_ranges_count = mem_range_index;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100176
177 /* TODO: Check for "reserved-memory" nodes. */
178}
179
Andrew Scull265ada92018-07-30 15:19:01 +0100180bool fdt_get_boot_params(paddr_t fdt_addr, struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100181{
Andrew Scull265ada92018-07-30 15:19:01 +0100182 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100183 struct fdt_node n;
184 bool ret = false;
185
186 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100187 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
188 MM_MODE_R);
189 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100190 dlog("Unable to map FDT header.\n");
191 goto err_unmap_fdt_header;
192 }
193
194 if (!fdt_root_node(&n, fdt)) {
195 dlog("FDT failed validation.\n");
196 goto err_unmap_fdt_header;
197 }
198
199 /* Map the rest of the fdt in. */
Andrew Scull80871322018-08-06 12:04:09 +0100200 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)),
201 MM_MODE_R);
202 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100203 dlog("Unable to map full FDT.\n");
204 goto err_unmap_fdt_header;
205 }
206
207 if (!fdt_find_child(&n, "")) {
208 dlog("Unable to find FDT root node.\n");
209 goto out_unmap_fdt;
210 }
211
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100212 p->mem_ranges_count = 0;
213 find_memory_ranges(&n, p);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100214
215 if (!find_initrd(&n, p)) {
216 goto out_unmap_fdt;
217 }
218
219 p->kernel_arg = (size_t)fdt;
220 ret = true;
221
222out_unmap_fdt:
Andrew Scull80871322018-08-06 12:04:09 +0100223 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100224 return ret;
225
226err_unmap_fdt_header:
Andrew Scull80871322018-08-06 12:04:09 +0100227 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100228 return false;
229}
230
Andrew Scull265ada92018-07-30 15:19:01 +0100231bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100232{
Andrew Scull265ada92018-07-30 15:19:01 +0100233 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100234 struct fdt_node n;
235 bool ret = false;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100236 size_t i;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100237
238 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100239 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
240 MM_MODE_R);
241 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100242 dlog("Unable to map FDT header.\n");
243 return false;
244 }
245
246 if (!fdt_root_node(&n, fdt)) {
247 dlog("FDT failed validation.\n");
248 goto err_unmap_fdt_header;
249 }
250
251 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull80871322018-08-06 12:04:09 +0100252 fdt = mm_identity_map(fdt_addr,
253 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
254 MM_MODE_R | MM_MODE_W);
255 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100256 dlog("Unable to map FDT in r/w mode.\n");
257 goto err_unmap_fdt_header;
258 }
259
260 if (!fdt_find_child(&n, "")) {
261 dlog("Unable to find FDT root node.\n");
262 goto out_unmap_fdt;
263 }
264
265 if (!fdt_find_child(&n, "chosen")) {
266 dlog("Unable to find 'chosen'\n");
267 goto out_unmap_fdt;
268 }
269
270 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100271 if (!fdt_write_number(&n, "linux,initrd-start",
272 pa_addr(p->initrd_begin))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100273 dlog("Unable to write linux,initrd-start\n");
274 goto out_unmap_fdt;
275 }
276
Andrew Scull265ada92018-07-30 15:19:01 +0100277 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100278 dlog("Unable to write linux,initrd-end\n");
279 goto out_unmap_fdt;
280 }
281
282 /* Patch fdt to reserve primary VM memory. */
Andrew Scull5991ec92018-10-08 14:55:02 +0100283 fdt_add_mem_reservation(fdt, pa_addr(layout_primary_begin()) & ~0xfffff,
284 0x80000);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100285
286 /* Patch fdt to reserve memory for secondary VMs. */
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100287 for (i = 0; i < p->reserved_ranges_count; ++i) {
288 fdt_add_mem_reservation(
289 fdt, pa_addr(p->reserved_ranges[i].begin),
290 pa_addr(p->reserved_ranges[i].end) -
291 pa_addr(p->reserved_ranges[i].begin));
292 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100293
294 ret = true;
295
296out_unmap_fdt:
297 /* Unmap FDT. */
Andrew Scull80871322018-08-06 12:04:09 +0100298 if (!mm_unmap(fdt_addr,
299 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE), 0)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100300 dlog("Unable to unmap writable FDT.\n");
301 return false;
302 }
303 return ret;
304
305err_unmap_fdt_header:
Andrew Scull80871322018-08-06 12:04:09 +0100306 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100307 return false;
308}