blob: 61b688dd6f7032e800b7e786b06b06024a1c17c9 [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
2 * Copyright 2018 Google LLC
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Andrew Scull18c78fc2018-08-20 12:57:41 +010017#include "hf/fdt_handler.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010018
Andrew Walbran4a53ba62019-03-05 17:26:12 +000019#include "hf/arch/std.h"
20
Andrew Scull18c78fc2018-08-20 12:57:41 +010021#include "hf/boot_params.h"
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000022#include "hf/cpu.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/dlog.h"
24#include "hf/fdt.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010025#include "hf/layout.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010026#include "hf/mm.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010027
28static uint64_t convert_number(const char *data, uint32_t size)
29{
30 union {
31 volatile uint64_t v;
32 char a[8];
33 } t;
34
35 switch (size) {
36 case sizeof(uint32_t):
37 return be32toh(*(uint32_t *)data);
38 case sizeof(uint64_t):
39 memcpy(t.a, data, sizeof(uint64_t));
40 return be64toh(t.v);
41 default:
42 return 0;
43 }
44}
45
46static bool fdt_read_number(const struct fdt_node *node, const char *name,
47 uint64_t *value)
48{
49 const char *data;
50 uint32_t size;
51
52 if (!fdt_read_property(node, name, &data, &size)) {
53 return false;
54 }
55
56 switch (size) {
57 case sizeof(uint32_t):
58 case sizeof(uint64_t):
59 *value = convert_number(data, size);
60 break;
61
62 default:
63 return false;
64 }
65
66 return true;
67}
68
69static bool fdt_write_number(struct fdt_node *node, const char *name,
70 uint64_t value)
71{
72 const char *data;
73 uint32_t size;
74 union {
75 volatile uint64_t v;
76 char a[8];
77 } t;
78
79 if (!fdt_read_property(node, name, &data, &size)) {
80 return false;
81 }
82
83 switch (size) {
84 case sizeof(uint32_t):
85 *(uint32_t *)data = be32toh(value);
86 break;
87
88 case sizeof(uint64_t):
89 t.v = be64toh(value);
90 memcpy((void *)data, t.a, sizeof(uint64_t));
91 break;
92
93 default:
94 return false;
95 }
96
97 return true;
98}
99
100/**
Andrew Walbranfb88f2c2018-09-18 15:23:49 +0100101 * Finds the memory region where initrd is stored, and updates the fdt node
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100102 * cursor to the node called "chosen".
103 */
Andrew Scullb401ba32018-11-09 10:30:54 +0000104bool fdt_find_initrd(struct fdt_node *n, paddr_t *begin, paddr_t *end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100105{
Andrew Scullb401ba32018-11-09 10:30:54 +0000106 uint64_t initrd_begin;
107 uint64_t initrd_end;
Andrew Scull265ada92018-07-30 15:19:01 +0100108
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100109 if (!fdt_find_child(n, "chosen")) {
110 dlog("Unable to find 'chosen'\n");
111 return false;
112 }
113
Andrew Scullb401ba32018-11-09 10:30:54 +0000114 if (!fdt_read_number(n, "linux,initrd-start", &initrd_begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100115 dlog("Unable to read linux,initrd-start\n");
116 return false;
117 }
118
Andrew Scullb401ba32018-11-09 10:30:54 +0000119 if (!fdt_read_number(n, "linux,initrd-end", &initrd_end)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100120 dlog("Unable to read linux,initrd-end\n");
121 return false;
122 }
123
Andrew Scullb401ba32018-11-09 10:30:54 +0000124 *begin = pa_init(initrd_begin);
125 *end = pa_init(initrd_end);
Andrew Scull265ada92018-07-30 15:19:01 +0100126
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100127 return true;
128}
129
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000130void fdt_find_cpus(const struct fdt_node *root, uint64_t *cpu_ids,
131 size_t *cpu_count)
132{
133 struct fdt_node n = *root;
134 const char *name;
135 uint64_t address_size;
136
137 *cpu_count = 0;
138
139 if (!fdt_find_child(&n, "cpus")) {
140 dlog("Unable to find 'cpus'\n");
141 return;
142 }
143
144 if (fdt_read_number(&n, "#address-cells", &address_size)) {
145 address_size *= sizeof(uint32_t);
146 } else {
147 address_size = sizeof(uint32_t);
148 }
149
150 if (!fdt_first_child(&n, &name)) {
151 return;
152 }
153
154 do {
155 const char *data;
156 uint32_t size;
157
158 if (!fdt_read_property(&n, "device_type", &data, &size) ||
159 size != sizeof("cpu") ||
160 memcmp(data, "cpu", sizeof("cpu")) != 0 ||
161 !fdt_read_property(&n, "reg", &data, &size)) {
162 continue;
163 }
164
165 /* Get all entries for this CPU. */
166 while (size >= address_size) {
167 if (*cpu_count >= MAX_CPUS) {
168 dlog("Found more than %d CPUs\n", MAX_CPUS);
169 return;
170 }
171
172 cpu_ids[(*cpu_count)++] =
173 convert_number(data, address_size);
174
175 size -= address_size;
176 data += address_size;
177 }
178 } while (fdt_next_sibling(&n, &name));
179}
180
Andrew Scullb401ba32018-11-09 10:30:54 +0000181void fdt_find_memory_ranges(const struct fdt_node *root, struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100182{
183 struct fdt_node n = *root;
184 const char *name;
185 uint64_t address_size;
186 uint64_t size_size;
187 uint64_t entry_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100188 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100189
190 /* Get the sizes of memory range addresses and sizes. */
191 if (fdt_read_number(&n, "#address-cells", &address_size)) {
192 address_size *= sizeof(uint32_t);
193 } else {
194 address_size = sizeof(uint32_t);
195 }
196
197 if (fdt_read_number(&n, "#size-cells", &size_size)) {
198 size_size *= sizeof(uint32_t);
199 } else {
200 size_size = sizeof(uint32_t);
201 }
202
203 entry_size = address_size + size_size;
204
205 /* Look for nodes with the device_type set to "memory". */
206 if (!fdt_first_child(&n, &name)) {
207 return;
208 }
209
210 do {
211 const char *data;
212 uint32_t size;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000213
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100214 if (!fdt_read_property(&n, "device_type", &data, &size) ||
215 size != sizeof("memory") ||
216 memcmp(data, "memory", sizeof("memory")) != 0 ||
217 !fdt_read_property(&n, "reg", &data, &size)) {
218 continue;
219 }
220
221 /* Traverse all memory ranges within this node. */
222 while (size >= entry_size) {
Andrew Scull265ada92018-07-30 15:19:01 +0100223 uintpaddr_t addr = convert_number(data, address_size);
224 size_t len =
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100225 convert_number(data + address_size, size_size);
226
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100227 if (mem_range_index < MAX_MEM_RANGES) {
228 p->mem_ranges[mem_range_index].begin =
229 pa_init(addr);
230 p->mem_ranges[mem_range_index].end =
231 pa_init(addr + len);
232 ++mem_range_index;
233 } else {
234 dlog("Found memory range %u in FDT but only "
235 "%u supported, ignoring additional range "
236 "of size %u.\n",
237 mem_range_index, MAX_MEM_RANGES, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100238 }
239
240 size -= entry_size;
241 data += entry_size;
242 }
243 } while (fdt_next_sibling(&n, &name));
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100244 p->mem_ranges_count = mem_range_index;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100245
246 /* TODO: Check for "reserved-memory" nodes. */
247}
248
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000249struct fdt_header *fdt_map(paddr_t fdt_addr, struct fdt_node *n,
250 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100251{
Andrew Scull265ada92018-07-30 15:19:01 +0100252 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100253
254 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100255 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000256 MM_MODE_R, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100257 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100258 dlog("Unable to map FDT header.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000259 return NULL;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100260 }
261
Andrew Scullb401ba32018-11-09 10:30:54 +0000262 if (!fdt_root_node(n, fdt)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100263 dlog("FDT failed validation.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000264 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100265 }
266
267 /* Map the rest of the fdt in. */
Andrew Scull80871322018-08-06 12:04:09 +0100268 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000269 MM_MODE_R, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100270 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100271 dlog("Unable to map full FDT.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000272 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100273 }
274
Andrew Scullb401ba32018-11-09 10:30:54 +0000275 return fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100276
Andrew Scullb401ba32018-11-09 10:30:54 +0000277fail:
Andrew Scullda241972019-01-05 18:17:48 +0000278 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), ppool);
Andrew Scullb401ba32018-11-09 10:30:54 +0000279 return NULL;
280}
281
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000282bool fdt_unmap(struct fdt_header *fdt, struct mpool *ppool)
Andrew Scullb401ba32018-11-09 10:30:54 +0000283{
284 paddr_t fdt_addr = pa_from_va(va_from_ptr(fdt));
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000285
Andrew Scullda241972019-01-05 18:17:48 +0000286 return mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)), ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100287}
288
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000289bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p,
290 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100291{
Andrew Scull265ada92018-07-30 15:19:01 +0100292 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100293 struct fdt_node n;
294 bool ret = false;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100295 size_t i;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100296
297 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100298 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000299 MM_MODE_R, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100300 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100301 dlog("Unable to map FDT header.\n");
302 return false;
303 }
304
305 if (!fdt_root_node(&n, fdt)) {
306 dlog("FDT failed validation.\n");
307 goto err_unmap_fdt_header;
308 }
309
310 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull80871322018-08-06 12:04:09 +0100311 fdt = mm_identity_map(fdt_addr,
312 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000313 MM_MODE_R | MM_MODE_W, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100314 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100315 dlog("Unable to map FDT in r/w mode.\n");
316 goto err_unmap_fdt_header;
317 }
318
319 if (!fdt_find_child(&n, "")) {
320 dlog("Unable to find FDT root node.\n");
321 goto out_unmap_fdt;
322 }
323
324 if (!fdt_find_child(&n, "chosen")) {
325 dlog("Unable to find 'chosen'\n");
326 goto out_unmap_fdt;
327 }
328
329 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100330 if (!fdt_write_number(&n, "linux,initrd-start",
331 pa_addr(p->initrd_begin))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100332 dlog("Unable to write linux,initrd-start\n");
333 goto out_unmap_fdt;
334 }
335
Andrew Scull265ada92018-07-30 15:19:01 +0100336 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100337 dlog("Unable to write linux,initrd-end\n");
338 goto out_unmap_fdt;
339 }
340
341 /* Patch fdt to reserve primary VM memory. */
Alfredo Mazzinghieb1997c2019-02-07 18:00:01 +0000342 fdt_add_mem_reservation(
343 fdt, align_down(pa_addr(layout_primary_begin()), 0x100000),
344 0x80000);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100345
346 /* Patch fdt to reserve memory for secondary VMs. */
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100347 for (i = 0; i < p->reserved_ranges_count; ++i) {
348 fdt_add_mem_reservation(
349 fdt, pa_addr(p->reserved_ranges[i].begin),
350 pa_addr(p->reserved_ranges[i].end) -
351 pa_addr(p->reserved_ranges[i].begin));
352 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100353
354 ret = true;
355
356out_unmap_fdt:
357 /* Unmap FDT. */
Andrew Scull80871322018-08-06 12:04:09 +0100358 if (!mm_unmap(fdt_addr,
Andrew Scullda241972019-01-05 18:17:48 +0000359 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000360 ppool)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100361 dlog("Unable to unmap writable FDT.\n");
362 return false;
363 }
364 return ret;
365
366err_unmap_fdt_header:
Andrew Scullda241972019-01-05 18:17:48 +0000367 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100368 return false;
369}