blob: 8af1401e5815c7258bc0905245bcc4cbbe3576ec [file] [log] [blame]
Andrew Scull18834872018-10-12 11:48:09 +01001/*
Andrew Walbran692b3252019-03-07 15:51:31 +00002 * Copyright 2018 The Hafnium Authors.
Andrew Scull18834872018-10-12 11:48:09 +01003 *
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 Scull18c78fc2018-08-20 12:57:41 +010019#include "hf/boot_params.h"
David Brazdil7a462ec2019-08-15 12:27:47 +010020#include "hf/check.h"
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000021#include "hf/cpu.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010022#include "hf/dlog.h"
23#include "hf/fdt.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010024#include "hf/layout.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010025#include "hf/mm.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010026#include "hf/std.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010027
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010028static 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):
David Brazdil7a462ec2019-08-15 12:27:47 +010041 CHECK(fdt_parse_number(data, size, value));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010042 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);
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010072 memcpy_s((void *)data, size, t.a, sizeof(uint64_t));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010073 break;
74
75 default:
76 return false;
77 }
78
79 return true;
80}
81
82/**
David Brazdil0dbb41f2019-09-09 18:03:35 +010083 * Finds the memory region where initrd is stored.
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010084 */
David Brazdil0dbb41f2019-09-09 18:03:35 +010085bool fdt_find_initrd(const struct fdt_node *root, paddr_t *begin, paddr_t *end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010086{
David Brazdil0dbb41f2019-09-09 18:03:35 +010087 struct fdt_node n = *root;
Andrew Scullb401ba32018-11-09 10:30:54 +000088 uint64_t initrd_begin;
89 uint64_t initrd_end;
Andrew Scull265ada92018-07-30 15:19:01 +010090
David Brazdil0dbb41f2019-09-09 18:03:35 +010091 if (!fdt_find_child(&n, "chosen")) {
Andrew Walbran17eebf92020-02-05 16:35:49 +000092 dlog_error("Unable to find 'chosen'\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010093 return false;
94 }
95
David Brazdil0dbb41f2019-09-09 18:03:35 +010096 if (!fdt_read_number(&n, "linux,initrd-start", &initrd_begin)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +000097 dlog_error("Unable to read linux,initrd-start\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010098 return false;
99 }
100
David Brazdil0dbb41f2019-09-09 18:03:35 +0100101 if (!fdt_read_number(&n, "linux,initrd-end", &initrd_end)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000102 dlog_error("Unable to read linux,initrd-end\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100103 return false;
104 }
105
Andrew Scullb401ba32018-11-09 10:30:54 +0000106 *begin = pa_init(initrd_begin);
107 *end = pa_init(initrd_end);
Andrew Scull265ada92018-07-30 15:19:01 +0100108
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100109 return true;
110}
111
David Brazdil0dbb41f2019-09-09 18:03:35 +0100112bool fdt_find_cpus(const struct fdt_node *root, cpu_id_t *cpu_ids,
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000113 size_t *cpu_count)
114{
115 struct fdt_node n = *root;
116 const char *name;
117 uint64_t address_size;
118
119 *cpu_count = 0;
120
121 if (!fdt_find_child(&n, "cpus")) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000122 dlog_error("Unable to find 'cpus'\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +0100123 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000124 }
125
126 if (fdt_read_number(&n, "#address-cells", &address_size)) {
127 address_size *= sizeof(uint32_t);
128 } else {
129 address_size = sizeof(uint32_t);
130 }
131
132 if (!fdt_first_child(&n, &name)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +0100133 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000134 }
135
136 do {
137 const char *data;
138 uint32_t size;
139
140 if (!fdt_read_property(&n, "device_type", &data, &size) ||
141 size != sizeof("cpu") ||
142 memcmp(data, "cpu", sizeof("cpu")) != 0 ||
143 !fdt_read_property(&n, "reg", &data, &size)) {
144 continue;
145 }
146
147 /* Get all entries for this CPU. */
148 while (size >= address_size) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100149 uint64_t value;
150
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000151 if (*cpu_count >= MAX_CPUS) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000152 dlog_error("Found more than %d CPUs\n",
153 MAX_CPUS);
David Brazdil0dbb41f2019-09-09 18:03:35 +0100154 return false;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000155 }
156
David Brazdil7a462ec2019-08-15 12:27:47 +0100157 if (!fdt_parse_number(data, address_size, &value)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000158 dlog_error("Could not parse CPU id\n");
David Brazdil0dbb41f2019-09-09 18:03:35 +0100159 return false;
David Brazdil7a462ec2019-08-15 12:27:47 +0100160 }
161 cpu_ids[(*cpu_count)++] = value;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000162
163 size -= address_size;
164 data += address_size;
165 }
166 } while (fdt_next_sibling(&n, &name));
David Brazdil0dbb41f2019-09-09 18:03:35 +0100167
168 return true;
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000169}
170
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000171bool fdt_find_memory_ranges(const struct fdt_node *root,
172 struct string *device_type,
173 struct mem_range *mem_ranges,
174 size_t *mem_ranges_count, size_t mem_range_limit)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100175{
176 struct fdt_node n = *root;
177 const char *name;
178 uint64_t address_size;
179 uint64_t size_size;
180 uint64_t entry_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100181 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100182
183 /* Get the sizes of memory range addresses and sizes. */
184 if (fdt_read_number(&n, "#address-cells", &address_size)) {
185 address_size *= sizeof(uint32_t);
186 } else {
187 address_size = sizeof(uint32_t);
188 }
189
190 if (fdt_read_number(&n, "#size-cells", &size_size)) {
191 size_size *= sizeof(uint32_t);
192 } else {
193 size_size = sizeof(uint32_t);
194 }
195
196 entry_size = address_size + size_size;
197
198 /* Look for nodes with the device_type set to "memory". */
199 if (!fdt_first_child(&n, &name)) {
David Brazdil0dbb41f2019-09-09 18:03:35 +0100200 return false;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100201 }
202
203 do {
204 const char *data;
205 uint32_t size;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000206
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100207 if (!fdt_read_property(&n, "device_type", &data, &size) ||
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000208 strncmp(data, string_data(device_type), STRING_MAX_SIZE) !=
209 0 ||
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100210 !fdt_read_property(&n, "reg", &data, &size)) {
211 continue;
212 }
213
214 /* Traverse all memory ranges within this node. */
215 while (size >= entry_size) {
David Brazdil7a462ec2019-08-15 12:27:47 +0100216 uintpaddr_t addr;
217 size_t len;
218
219 CHECK(fdt_parse_number(data, address_size, &addr));
220 CHECK(fdt_parse_number(data + address_size, size_size,
221 &len));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100222
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000223 if (mem_range_index < mem_range_limit) {
224 mem_ranges[mem_range_index].begin =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100225 pa_init(addr);
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000226 mem_ranges[mem_range_index].end =
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100227 pa_init(addr + len);
228 ++mem_range_index;
229 } else {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000230 dlog_error(
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000231 "Found %s range %u in FDT but only %u "
232 "supported, ignoring additional range "
233 "of size %u.\n",
234 string_data(device_type),
235 mem_range_index, mem_range_limit, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100236 }
237
238 size -= entry_size;
239 data += entry_size;
240 }
241 } while (fdt_next_sibling(&n, &name));
Andrew Scull6c9a4ab2020-01-27 17:09:12 +0000242 *mem_ranges_count = mem_range_index;
David Brazdil0dbb41f2019-09-09 18:03:35 +0100243
244 return true;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100245}
246
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100247struct fdt_header *fdt_map(struct mm_stage1_locked stage1_locked,
248 paddr_t fdt_addr, struct fdt_node *n,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000249 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100250{
Andrew Scull265ada92018-07-30 15:19:01 +0100251 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100252
253 /* Map the fdt header in. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100254 fdt = mm_identity_map(stage1_locked, fdt_addr,
255 pa_add(fdt_addr, fdt_header_size()), MM_MODE_R,
256 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100257 if (!fdt) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000258 dlog_error("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)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000263 dlog_error("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 Scull3c0a90a2019-07-01 11:55:53 +0100268 fdt = mm_identity_map(stage1_locked, fdt_addr,
269 pa_add(fdt_addr, fdt_total_size(fdt)), MM_MODE_R,
270 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100271 if (!fdt) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000272 dlog_error("Unable to map full FDT.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000273 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100274 }
275
Andrew Scullb401ba32018-11-09 10:30:54 +0000276 return fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100277
Andrew Scullb401ba32018-11-09 10:30:54 +0000278fail:
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100279 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_header_size()),
280 ppool);
Andrew Scullb401ba32018-11-09 10:30:54 +0000281 return NULL;
282}
283
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100284bool fdt_unmap(struct mm_stage1_locked stage1_locked, struct fdt_header *fdt,
285 struct mpool *ppool)
Andrew Scullb401ba32018-11-09 10:30:54 +0000286{
287 paddr_t fdt_addr = pa_from_va(va_from_ptr(fdt));
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000288
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100289 return mm_unmap(stage1_locked, fdt_addr,
290 pa_add(fdt_addr, fdt_total_size(fdt)), ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100291}
292
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100293bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
294 struct boot_params_update *p, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100295{
Andrew Scull265ada92018-07-30 15:19:01 +0100296 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100297 struct fdt_node n;
298 bool ret = false;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100299 size_t i;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100300
301 /* Map the fdt header in. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100302 fdt = mm_identity_map(stage1_locked, fdt_addr,
303 pa_add(fdt_addr, fdt_header_size()), MM_MODE_R,
304 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100305 if (!fdt) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000306 dlog_error("Unable to map FDT header.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100307 return false;
308 }
309
310 if (!fdt_root_node(&n, fdt)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000311 dlog_error("FDT failed validation.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100312 goto err_unmap_fdt_header;
313 }
314
315 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100316 fdt = mm_identity_map(stage1_locked, fdt_addr,
Andrew Scull80871322018-08-06 12:04:09 +0100317 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000318 MM_MODE_R | MM_MODE_W, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100319 if (!fdt) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000320 dlog_error("Unable to map FDT in r/w mode.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100321 goto err_unmap_fdt_header;
322 }
323
324 if (!fdt_find_child(&n, "")) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000325 dlog_error("Unable to find FDT root node.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100326 goto out_unmap_fdt;
327 }
328
329 if (!fdt_find_child(&n, "chosen")) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000330 dlog_error("Unable to find 'chosen'\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100331 goto out_unmap_fdt;
332 }
333
334 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100335 if (!fdt_write_number(&n, "linux,initrd-start",
336 pa_addr(p->initrd_begin))) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000337 dlog_error("Unable to write linux,initrd-start\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100338 goto out_unmap_fdt;
339 }
340
Andrew Scull265ada92018-07-30 15:19:01 +0100341 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000342 dlog_error("Unable to write linux,initrd-end\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100343 goto out_unmap_fdt;
344 }
345
Andrew Walbran401ff562019-04-09 15:43:31 +0100346 /*
347 * Patch FDT to reserve hypervisor memory so the primary VM doesn't try
348 * to use it.
349 */
Alfredo Mazzinghieb1997c2019-02-07 18:00:01 +0000350 fdt_add_mem_reservation(
Andrew Walbran401ff562019-04-09 15:43:31 +0100351 fdt, pa_addr(layout_text_begin()),
Andrew Walbran2cb43392019-04-17 12:52:45 +0100352 pa_difference(layout_text_begin(), layout_text_end()));
Andrew Walbran401ff562019-04-09 15:43:31 +0100353 fdt_add_mem_reservation(
354 fdt, pa_addr(layout_rodata_begin()),
Andrew Walbran2cb43392019-04-17 12:52:45 +0100355 pa_difference(layout_rodata_begin(), layout_rodata_end()));
Andrew Walbran401ff562019-04-09 15:43:31 +0100356 fdt_add_mem_reservation(
357 fdt, pa_addr(layout_data_begin()),
Andrew Walbran2cb43392019-04-17 12:52:45 +0100358 pa_difference(layout_data_begin(), layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100359
Andrew Walbran401ff562019-04-09 15:43:31 +0100360 /* Patch FDT to reserve memory for secondary VMs. */
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100361 for (i = 0; i < p->reserved_ranges_count; ++i) {
362 fdt_add_mem_reservation(
363 fdt, pa_addr(p->reserved_ranges[i].begin),
364 pa_addr(p->reserved_ranges[i].end) -
365 pa_addr(p->reserved_ranges[i].begin));
366 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100367
368 ret = true;
369
370out_unmap_fdt:
371 /* Unmap FDT. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100372 if (!mm_unmap(stage1_locked, fdt_addr,
Andrew Scullda241972019-01-05 18:17:48 +0000373 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000374 ppool)) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000375 dlog_error("Unable to unmap writable FDT.\n");
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100376 return false;
377 }
378 return ret;
379
380err_unmap_fdt_header:
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100381 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_header_size()),
382 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100383 return false;
384}