blob: fa2f9b6d450207451adf7f6449b5da64194c5b9b [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{
87 if (!fdt_find_child(n, "chosen")) {
88 dlog("Unable to find 'chosen'\n");
89 return false;
90 }
91
92 if (!fdt_read_number(n, "linux,initrd-start", &p->initrd_begin)) {
93 dlog("Unable to read linux,initrd-start\n");
94 return false;
95 }
96
97 if (!fdt_read_number(n, "linux,initrd-end", &p->initrd_end)) {
98 dlog("Unable to read linux,initrd-end\n");
99 return false;
100 }
101
102 return true;
103}
104
105static void find_memory_range(const struct fdt_node *root,
106 struct boot_params *p)
107{
108 struct fdt_node n = *root;
109 const char *name;
110 uint64_t address_size;
111 uint64_t size_size;
112 uint64_t entry_size;
113
114 /* Get the sizes of memory range addresses and sizes. */
115 if (fdt_read_number(&n, "#address-cells", &address_size)) {
116 address_size *= sizeof(uint32_t);
117 } else {
118 address_size = sizeof(uint32_t);
119 }
120
121 if (fdt_read_number(&n, "#size-cells", &size_size)) {
122 size_size *= sizeof(uint32_t);
123 } else {
124 size_size = sizeof(uint32_t);
125 }
126
127 entry_size = address_size + size_size;
128
129 /* Look for nodes with the device_type set to "memory". */
130 if (!fdt_first_child(&n, &name)) {
131 return;
132 }
133
134 do {
135 const char *data;
136 uint32_t size;
137 if (!fdt_read_property(&n, "device_type", &data, &size) ||
138 size != sizeof("memory") ||
139 memcmp(data, "memory", sizeof("memory")) != 0 ||
140 !fdt_read_property(&n, "reg", &data, &size)) {
141 continue;
142 }
143
144 /* Traverse all memory ranges within this node. */
145 while (size >= entry_size) {
146 uint64_t addr = convert_number(data, address_size);
147 uint64_t len =
148 convert_number(data + address_size, size_size);
149
150 if (len > p->mem_end - p->mem_begin) {
151 /* Remember the largest range we've found. */
152 p->mem_begin = addr;
153 p->mem_end = addr + len;
154 }
155
156 size -= entry_size;
157 data += entry_size;
158 }
159 } while (fdt_next_sibling(&n, &name));
160
161 /* TODO: Check for "reserved-memory" nodes. */
162}
163
164bool fdt_get_boot_params(struct fdt_header *fdt, struct boot_params *p)
165{
166 struct fdt_node n;
167 bool ret = false;
168
169 /* Map the fdt header in. */
170 if (!mm_map((vaddr_t)fdt, (vaddr_t)fdt + fdt_header_size(),
171 (paddr_t)fdt, MM_MODE_R)) {
172 dlog("Unable to map FDT header.\n");
173 goto err_unmap_fdt_header;
174 }
175
176 if (!fdt_root_node(&n, fdt)) {
177 dlog("FDT failed validation.\n");
178 goto err_unmap_fdt_header;
179 }
180
181 /* Map the rest of the fdt in. */
182 if (!mm_map((vaddr_t)fdt, (vaddr_t)fdt + fdt_total_size(fdt),
183 (paddr_t)fdt, MM_MODE_R)) {
184 dlog("Unable to map full FDT.\n");
185 goto err_unmap_fdt_header;
186 }
187
188 if (!fdt_find_child(&n, "")) {
189 dlog("Unable to find FDT root node.\n");
190 goto out_unmap_fdt;
191 }
192
193 p->mem_begin = 0;
194 p->mem_end = 0;
195 find_memory_range(&n, p);
196
197 if (!find_initrd(&n, p)) {
198 goto out_unmap_fdt;
199 }
200
201 p->kernel_arg = (size_t)fdt;
202 ret = true;
203
204out_unmap_fdt:
205 mm_unmap((vaddr_t)fdt, (vaddr_t)fdt + fdt_total_size(fdt), 0);
206 return ret;
207
208err_unmap_fdt_header:
209 mm_unmap((vaddr_t)fdt, (vaddr_t)fdt + fdt_header_size(), 0);
210 return false;
211}
212
213bool fdt_patch(struct fdt_header *fdt, struct boot_params_update *p)
214{
215 struct fdt_node n;
216 bool ret = false;
217
218 /* Map the fdt header in. */
219 if (!mm_map((vaddr_t)fdt, (vaddr_t)fdt + fdt_header_size(),
220 (paddr_t)fdt, MM_MODE_R)) {
221 dlog("Unable to map FDT header.\n");
222 return false;
223 }
224
225 if (!fdt_root_node(&n, fdt)) {
226 dlog("FDT failed validation.\n");
227 goto err_unmap_fdt_header;
228 }
229
230 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
231 if (!mm_map((vaddr_t)fdt,
232 (vaddr_t)fdt + fdt_total_size(fdt) + PAGE_SIZE,
233 (paddr_t)fdt, MM_MODE_R | MM_MODE_W)) {
234 dlog("Unable to map FDT in r/w mode.\n");
235 goto err_unmap_fdt_header;
236 }
237
238 if (!fdt_find_child(&n, "")) {
239 dlog("Unable to find FDT root node.\n");
240 goto out_unmap_fdt;
241 }
242
243 if (!fdt_find_child(&n, "chosen")) {
244 dlog("Unable to find 'chosen'\n");
245 goto out_unmap_fdt;
246 }
247
248 /* Patch FDT to point to new ramdisk. */
249 if (!fdt_write_number(&n, "linux,initrd-start", p->initrd_begin)) {
250 dlog("Unable to write linux,initrd-start\n");
251 goto out_unmap_fdt;
252 }
253
254 if (!fdt_write_number(&n, "linux,initrd-end", p->initrd_end)) {
255 dlog("Unable to write linux,initrd-end\n");
256 goto out_unmap_fdt;
257 }
258
259 /* Patch fdt to reserve primary VM memory. */
260 {
261 size_t tmp = (size_t)&plat_update_boot_params;
262 tmp = (tmp + 0x80000 - 1) & ~(0x80000 - 1);
263 fdt_add_mem_reservation(fdt, tmp & ~0xfffff, 0x80000);
264 }
265
266 /* Patch fdt to reserve memory for secondary VMs. */
267 fdt_add_mem_reservation(fdt, p->reserved_begin,
268 p->reserved_end - p->reserved_begin);
269
270 ret = true;
271
272out_unmap_fdt:
273 /* Unmap FDT. */
274 if (!mm_unmap((vaddr_t)fdt,
275 (vaddr_t)fdt + fdt_total_size(fdt) + PAGE_SIZE, 0)) {
276 dlog("Unable to unmap writable FDT.\n");
277 return false;
278 }
279 return ret;
280
281err_unmap_fdt_header:
282 mm_unmap((vaddr_t)fdt, (vaddr_t)fdt + fdt_header_size(), 0);
283 return false;
284}