blob: a87226ec1bcfd823a5f5e815389e2ccccf57e44f [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"
Andrew Scullbb3ab6c2018-11-26 20:38:49 +000020#include "hf/cpu.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010021#include "hf/dlog.h"
22#include "hf/fdt.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010023#include "hf/layout.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010024#include "hf/mm.h"
Andrew Scull8d9e1212019-04-05 13:52:55 +010025#include "hf/std.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010026
27static uint64_t convert_number(const char *data, uint32_t size)
28{
29 union {
30 volatile uint64_t v;
31 char a[8];
32 } t;
33
34 switch (size) {
35 case sizeof(uint32_t):
36 return be32toh(*(uint32_t *)data);
37 case sizeof(uint64_t):
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010038 memcpy_s(t.a, sizeof(t.a), data, sizeof(uint64_t));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010039 return be64toh(t.v);
40 default:
41 return 0;
42 }
43}
44
45static bool fdt_read_number(const struct fdt_node *node, const char *name,
46 uint64_t *value)
47{
48 const char *data;
49 uint32_t size;
50
51 if (!fdt_read_property(node, name, &data, &size)) {
52 return false;
53 }
54
55 switch (size) {
56 case sizeof(uint32_t):
57 case sizeof(uint64_t):
58 *value = convert_number(data, size);
59 break;
60
61 default:
62 return false;
63 }
64
65 return true;
66}
67
68static bool fdt_write_number(struct fdt_node *node, const char *name,
69 uint64_t value)
70{
71 const char *data;
72 uint32_t size;
73 union {
74 volatile uint64_t v;
75 char a[8];
76 } t;
77
78 if (!fdt_read_property(node, name, &data, &size)) {
79 return false;
80 }
81
82 switch (size) {
83 case sizeof(uint32_t):
84 *(uint32_t *)data = be32toh(value);
85 break;
86
87 case sizeof(uint64_t):
88 t.v = be64toh(value);
Andrew Sculla1aa2ba2019-04-05 11:49:02 +010089 memcpy_s((void *)data, size, t.a, sizeof(uint64_t));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010090 break;
91
92 default:
93 return false;
94 }
95
96 return true;
97}
98
99/**
Andrew Walbranfb88f2c2018-09-18 15:23:49 +0100100 * Finds the memory region where initrd is stored, and updates the fdt node
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100101 * cursor to the node called "chosen".
102 */
Andrew Scullb401ba32018-11-09 10:30:54 +0000103bool fdt_find_initrd(struct fdt_node *n, paddr_t *begin, paddr_t *end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100104{
Andrew Scullb401ba32018-11-09 10:30:54 +0000105 uint64_t initrd_begin;
106 uint64_t initrd_end;
Andrew Scull265ada92018-07-30 15:19:01 +0100107
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100108 if (!fdt_find_child(n, "chosen")) {
109 dlog("Unable to find 'chosen'\n");
110 return false;
111 }
112
Andrew Scullb401ba32018-11-09 10:30:54 +0000113 if (!fdt_read_number(n, "linux,initrd-start", &initrd_begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100114 dlog("Unable to read linux,initrd-start\n");
115 return false;
116 }
117
Andrew Scullb401ba32018-11-09 10:30:54 +0000118 if (!fdt_read_number(n, "linux,initrd-end", &initrd_end)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100119 dlog("Unable to read linux,initrd-end\n");
120 return false;
121 }
122
Andrew Scullb401ba32018-11-09 10:30:54 +0000123 *begin = pa_init(initrd_begin);
124 *end = pa_init(initrd_end);
Andrew Scull265ada92018-07-30 15:19:01 +0100125
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100126 return true;
127}
128
Andrew Scullbb3ab6c2018-11-26 20:38:49 +0000129void fdt_find_cpus(const struct fdt_node *root, uint64_t *cpu_ids,
130 size_t *cpu_count)
131{
132 struct fdt_node n = *root;
133 const char *name;
134 uint64_t address_size;
135
136 *cpu_count = 0;
137
138 if (!fdt_find_child(&n, "cpus")) {
139 dlog("Unable to find 'cpus'\n");
140 return;
141 }
142
143 if (fdt_read_number(&n, "#address-cells", &address_size)) {
144 address_size *= sizeof(uint32_t);
145 } else {
146 address_size = sizeof(uint32_t);
147 }
148
149 if (!fdt_first_child(&n, &name)) {
150 return;
151 }
152
153 do {
154 const char *data;
155 uint32_t size;
156
157 if (!fdt_read_property(&n, "device_type", &data, &size) ||
158 size != sizeof("cpu") ||
159 memcmp(data, "cpu", sizeof("cpu")) != 0 ||
160 !fdt_read_property(&n, "reg", &data, &size)) {
161 continue;
162 }
163
164 /* Get all entries for this CPU. */
165 while (size >= address_size) {
166 if (*cpu_count >= MAX_CPUS) {
167 dlog("Found more than %d CPUs\n", MAX_CPUS);
168 return;
169 }
170
171 cpu_ids[(*cpu_count)++] =
172 convert_number(data, address_size);
173
174 size -= address_size;
175 data += address_size;
176 }
177 } while (fdt_next_sibling(&n, &name));
178}
179
Andrew Scullb401ba32018-11-09 10:30:54 +0000180void fdt_find_memory_ranges(const struct fdt_node *root, struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100181{
182 struct fdt_node n = *root;
183 const char *name;
184 uint64_t address_size;
185 uint64_t size_size;
186 uint64_t entry_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100187 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100188
189 /* Get the sizes of memory range addresses and sizes. */
190 if (fdt_read_number(&n, "#address-cells", &address_size)) {
191 address_size *= sizeof(uint32_t);
192 } else {
193 address_size = sizeof(uint32_t);
194 }
195
196 if (fdt_read_number(&n, "#size-cells", &size_size)) {
197 size_size *= sizeof(uint32_t);
198 } else {
199 size_size = sizeof(uint32_t);
200 }
201
202 entry_size = address_size + size_size;
203
204 /* Look for nodes with the device_type set to "memory". */
205 if (!fdt_first_child(&n, &name)) {
206 return;
207 }
208
209 do {
210 const char *data;
211 uint32_t size;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000212
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100213 if (!fdt_read_property(&n, "device_type", &data, &size) ||
214 size != sizeof("memory") ||
215 memcmp(data, "memory", sizeof("memory")) != 0 ||
216 !fdt_read_property(&n, "reg", &data, &size)) {
217 continue;
218 }
219
220 /* Traverse all memory ranges within this node. */
221 while (size >= entry_size) {
Andrew Scull265ada92018-07-30 15:19:01 +0100222 uintpaddr_t addr = convert_number(data, address_size);
223 size_t len =
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100224 convert_number(data + address_size, size_size);
225
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100226 if (mem_range_index < MAX_MEM_RANGES) {
227 p->mem_ranges[mem_range_index].begin =
228 pa_init(addr);
229 p->mem_ranges[mem_range_index].end =
230 pa_init(addr + len);
231 ++mem_range_index;
232 } else {
233 dlog("Found memory range %u in FDT but only "
234 "%u supported, ignoring additional range "
235 "of size %u.\n",
236 mem_range_index, MAX_MEM_RANGES, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100237 }
238
239 size -= entry_size;
240 data += entry_size;
241 }
242 } while (fdt_next_sibling(&n, &name));
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100243 p->mem_ranges_count = mem_range_index;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100244
245 /* TODO: Check for "reserved-memory" nodes. */
246}
247
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100248struct fdt_header *fdt_map(struct mm_stage1_locked stage1_locked,
249 paddr_t fdt_addr, struct fdt_node *n,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000250 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 Scull3c0a90a2019-07-01 11:55:53 +0100255 fdt = mm_identity_map(stage1_locked, fdt_addr,
256 pa_add(fdt_addr, fdt_header_size()), MM_MODE_R,
257 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100258 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100259 dlog("Unable to map FDT header.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000260 return NULL;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100261 }
262
Andrew Scullb401ba32018-11-09 10:30:54 +0000263 if (!fdt_root_node(n, fdt)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100264 dlog("FDT failed validation.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000265 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100266 }
267
268 /* Map the rest of the fdt in. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100269 fdt = mm_identity_map(stage1_locked, fdt_addr,
270 pa_add(fdt_addr, fdt_total_size(fdt)), MM_MODE_R,
271 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100272 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100273 dlog("Unable to map full FDT.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000274 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100275 }
276
Andrew Scullb401ba32018-11-09 10:30:54 +0000277 return fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100278
Andrew Scullb401ba32018-11-09 10:30:54 +0000279fail:
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100280 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_header_size()),
281 ppool);
Andrew Scullb401ba32018-11-09 10:30:54 +0000282 return NULL;
283}
284
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100285bool fdt_unmap(struct mm_stage1_locked stage1_locked, struct fdt_header *fdt,
286 struct mpool *ppool)
Andrew Scullb401ba32018-11-09 10:30:54 +0000287{
288 paddr_t fdt_addr = pa_from_va(va_from_ptr(fdt));
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000289
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100290 return mm_unmap(stage1_locked, fdt_addr,
291 pa_add(fdt_addr, fdt_total_size(fdt)), ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100292}
293
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100294bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
295 struct boot_params_update *p, struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100296{
Andrew Scull265ada92018-07-30 15:19:01 +0100297 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100298 struct fdt_node n;
299 bool ret = false;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100300 size_t i;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100301
302 /* Map the fdt header in. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100303 fdt = mm_identity_map(stage1_locked, fdt_addr,
304 pa_add(fdt_addr, fdt_header_size()), MM_MODE_R,
305 ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100306 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100307 dlog("Unable to map FDT header.\n");
308 return false;
309 }
310
311 if (!fdt_root_node(&n, fdt)) {
312 dlog("FDT failed validation.\n");
313 goto err_unmap_fdt_header;
314 }
315
316 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100317 fdt = mm_identity_map(stage1_locked, fdt_addr,
Andrew Scull80871322018-08-06 12:04:09 +0100318 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000319 MM_MODE_R | MM_MODE_W, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100320 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100321 dlog("Unable to map FDT in r/w mode.\n");
322 goto err_unmap_fdt_header;
323 }
324
325 if (!fdt_find_child(&n, "")) {
326 dlog("Unable to find FDT root node.\n");
327 goto out_unmap_fdt;
328 }
329
330 if (!fdt_find_child(&n, "chosen")) {
331 dlog("Unable to find 'chosen'\n");
332 goto out_unmap_fdt;
333 }
334
335 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100336 if (!fdt_write_number(&n, "linux,initrd-start",
337 pa_addr(p->initrd_begin))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100338 dlog("Unable to write linux,initrd-start\n");
339 goto out_unmap_fdt;
340 }
341
Andrew Scull265ada92018-07-30 15:19:01 +0100342 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100343 dlog("Unable to write linux,initrd-end\n");
344 goto out_unmap_fdt;
345 }
346
Andrew Walbran401ff562019-04-09 15:43:31 +0100347 /*
348 * Patch FDT to reserve hypervisor memory so the primary VM doesn't try
349 * to use it.
350 */
Alfredo Mazzinghieb1997c2019-02-07 18:00:01 +0000351 fdt_add_mem_reservation(
Andrew Walbran401ff562019-04-09 15:43:31 +0100352 fdt, pa_addr(layout_text_begin()),
Andrew Walbran2cb43392019-04-17 12:52:45 +0100353 pa_difference(layout_text_begin(), layout_text_end()));
Andrew Walbran401ff562019-04-09 15:43:31 +0100354 fdt_add_mem_reservation(
355 fdt, pa_addr(layout_rodata_begin()),
Andrew Walbran2cb43392019-04-17 12:52:45 +0100356 pa_difference(layout_rodata_begin(), layout_rodata_end()));
Andrew Walbran401ff562019-04-09 15:43:31 +0100357 fdt_add_mem_reservation(
358 fdt, pa_addr(layout_data_begin()),
Andrew Walbran2cb43392019-04-17 12:52:45 +0100359 pa_difference(layout_data_begin(), layout_data_end()));
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100360
Andrew Walbran401ff562019-04-09 15:43:31 +0100361 /* Patch FDT to reserve memory for secondary VMs. */
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100362 for (i = 0; i < p->reserved_ranges_count; ++i) {
363 fdt_add_mem_reservation(
364 fdt, pa_addr(p->reserved_ranges[i].begin),
365 pa_addr(p->reserved_ranges[i].end) -
366 pa_addr(p->reserved_ranges[i].begin));
367 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100368
369 ret = true;
370
371out_unmap_fdt:
372 /* Unmap FDT. */
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100373 if (!mm_unmap(stage1_locked, fdt_addr,
Andrew Scullda241972019-01-05 18:17:48 +0000374 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000375 ppool)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100376 dlog("Unable to unmap writable FDT.\n");
377 return false;
378 }
379 return ret;
380
381err_unmap_fdt_header:
Andrew Scull3c0a90a2019-07-01 11:55:53 +0100382 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_header_size()),
383 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100384 return false;
385}