blob: d0400a89d7506347907ec46c4afb0970815807cd [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 Scull18c78fc2018-08-20 12:57:41 +010019#include "hf/boot_params.h"
20#include "hf/dlog.h"
21#include "hf/fdt.h"
Andrew Scull5991ec92018-10-08 14:55:02 +010022#include "hf/layout.h"
Andrew Scull18c78fc2018-08-20 12:57:41 +010023#include "hf/mm.h"
24#include "hf/std.h"
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +010025
26static uint64_t convert_number(const char *data, uint32_t size)
27{
28 union {
29 volatile uint64_t v;
30 char a[8];
31 } t;
32
33 switch (size) {
34 case sizeof(uint32_t):
35 return be32toh(*(uint32_t *)data);
36 case sizeof(uint64_t):
37 memcpy(t.a, data, sizeof(uint64_t));
38 return be64toh(t.v);
39 default:
40 return 0;
41 }
42}
43
44static bool fdt_read_number(const struct fdt_node *node, const char *name,
45 uint64_t *value)
46{
47 const char *data;
48 uint32_t size;
49
50 if (!fdt_read_property(node, name, &data, &size)) {
51 return false;
52 }
53
54 switch (size) {
55 case sizeof(uint32_t):
56 case sizeof(uint64_t):
57 *value = convert_number(data, size);
58 break;
59
60 default:
61 return false;
62 }
63
64 return true;
65}
66
67static bool fdt_write_number(struct fdt_node *node, const char *name,
68 uint64_t value)
69{
70 const char *data;
71 uint32_t size;
72 union {
73 volatile uint64_t v;
74 char a[8];
75 } t;
76
77 if (!fdt_read_property(node, name, &data, &size)) {
78 return false;
79 }
80
81 switch (size) {
82 case sizeof(uint32_t):
83 *(uint32_t *)data = be32toh(value);
84 break;
85
86 case sizeof(uint64_t):
87 t.v = be64toh(value);
88 memcpy((void *)data, t.a, sizeof(uint64_t));
89 break;
90
91 default:
92 return false;
93 }
94
95 return true;
96}
97
98/**
Andrew Walbranfb88f2c2018-09-18 15:23:49 +010099 * Finds the memory region where initrd is stored, and updates the fdt node
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100100 * cursor to the node called "chosen".
101 */
Andrew Scullb401ba32018-11-09 10:30:54 +0000102bool fdt_find_initrd(struct fdt_node *n, paddr_t *begin, paddr_t *end)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100103{
Andrew Scullb401ba32018-11-09 10:30:54 +0000104 uint64_t initrd_begin;
105 uint64_t initrd_end;
Andrew Scull265ada92018-07-30 15:19:01 +0100106
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100107 if (!fdt_find_child(n, "chosen")) {
108 dlog("Unable to find 'chosen'\n");
109 return false;
110 }
111
Andrew Scullb401ba32018-11-09 10:30:54 +0000112 if (!fdt_read_number(n, "linux,initrd-start", &initrd_begin)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100113 dlog("Unable to read linux,initrd-start\n");
114 return false;
115 }
116
Andrew Scullb401ba32018-11-09 10:30:54 +0000117 if (!fdt_read_number(n, "linux,initrd-end", &initrd_end)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100118 dlog("Unable to read linux,initrd-end\n");
119 return false;
120 }
121
Andrew Scullb401ba32018-11-09 10:30:54 +0000122 *begin = pa_init(initrd_begin);
123 *end = pa_init(initrd_end);
Andrew Scull265ada92018-07-30 15:19:01 +0100124
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100125 return true;
126}
127
Andrew Scullb401ba32018-11-09 10:30:54 +0000128void fdt_find_memory_ranges(const struct fdt_node *root, struct boot_params *p)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100129{
130 struct fdt_node n = *root;
131 const char *name;
132 uint64_t address_size;
133 uint64_t size_size;
134 uint64_t entry_size;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100135 size_t mem_range_index = 0;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100136
137 /* Get the sizes of memory range addresses and sizes. */
138 if (fdt_read_number(&n, "#address-cells", &address_size)) {
139 address_size *= sizeof(uint32_t);
140 } else {
141 address_size = sizeof(uint32_t);
142 }
143
144 if (fdt_read_number(&n, "#size-cells", &size_size)) {
145 size_size *= sizeof(uint32_t);
146 } else {
147 size_size = sizeof(uint32_t);
148 }
149
150 entry_size = address_size + size_size;
151
152 /* Look for nodes with the device_type set to "memory". */
153 if (!fdt_first_child(&n, &name)) {
154 return;
155 }
156
157 do {
158 const char *data;
159 uint32_t size;
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000160
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100161 if (!fdt_read_property(&n, "device_type", &data, &size) ||
162 size != sizeof("memory") ||
163 memcmp(data, "memory", sizeof("memory")) != 0 ||
164 !fdt_read_property(&n, "reg", &data, &size)) {
165 continue;
166 }
167
168 /* Traverse all memory ranges within this node. */
169 while (size >= entry_size) {
Andrew Scull265ada92018-07-30 15:19:01 +0100170 uintpaddr_t addr = convert_number(data, address_size);
171 size_t len =
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100172 convert_number(data + address_size, size_size);
173
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100174 if (mem_range_index < MAX_MEM_RANGES) {
175 p->mem_ranges[mem_range_index].begin =
176 pa_init(addr);
177 p->mem_ranges[mem_range_index].end =
178 pa_init(addr + len);
179 ++mem_range_index;
180 } else {
181 dlog("Found memory range %u in FDT but only "
182 "%u supported, ignoring additional range "
183 "of size %u.\n",
184 mem_range_index, MAX_MEM_RANGES, len);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100185 }
186
187 size -= entry_size;
188 data += entry_size;
189 }
190 } while (fdt_next_sibling(&n, &name));
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100191 p->mem_ranges_count = mem_range_index;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100192
193 /* TODO: Check for "reserved-memory" nodes. */
194}
195
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000196struct fdt_header *fdt_map(paddr_t fdt_addr, struct fdt_node *n,
197 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100198{
Andrew Scull265ada92018-07-30 15:19:01 +0100199 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100200
201 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100202 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000203 MM_MODE_R, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100204 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100205 dlog("Unable to map FDT header.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000206 return NULL;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100207 }
208
Andrew Scullb401ba32018-11-09 10:30:54 +0000209 if (!fdt_root_node(n, fdt)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100210 dlog("FDT failed validation.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000211 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100212 }
213
214 /* Map the rest of the fdt in. */
Andrew Scull80871322018-08-06 12:04:09 +0100215 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000216 MM_MODE_R, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100217 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100218 dlog("Unable to map full FDT.\n");
Andrew Scullb401ba32018-11-09 10:30:54 +0000219 goto fail;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100220 }
221
Andrew Scullb401ba32018-11-09 10:30:54 +0000222 return fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100223
Andrew Scullb401ba32018-11-09 10:30:54 +0000224fail:
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000225 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0, ppool);
Andrew Scullb401ba32018-11-09 10:30:54 +0000226 return NULL;
227}
228
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000229bool fdt_unmap(struct fdt_header *fdt, struct mpool *ppool)
Andrew Scullb401ba32018-11-09 10:30:54 +0000230{
231 paddr_t fdt_addr = pa_from_va(va_from_ptr(fdt));
Wedson Almeida Filho81568c42019-01-04 13:33:02 +0000232
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000233 return mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_total_size(fdt)), 0,
234 ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100235}
236
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000237bool fdt_patch(paddr_t fdt_addr, struct boot_params_update *p,
238 struct mpool *ppool)
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100239{
Andrew Scull265ada92018-07-30 15:19:01 +0100240 struct fdt_header *fdt;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100241 struct fdt_node n;
242 bool ret = false;
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100243 size_t i;
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100244
245 /* Map the fdt header in. */
Andrew Scull80871322018-08-06 12:04:09 +0100246 fdt = mm_identity_map(fdt_addr, pa_add(fdt_addr, fdt_header_size()),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000247 MM_MODE_R, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100248 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100249 dlog("Unable to map FDT header.\n");
250 return false;
251 }
252
253 if (!fdt_root_node(&n, fdt)) {
254 dlog("FDT failed validation.\n");
255 goto err_unmap_fdt_header;
256 }
257
258 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
Andrew Scull80871322018-08-06 12:04:09 +0100259 fdt = mm_identity_map(fdt_addr,
260 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE),
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000261 MM_MODE_R | MM_MODE_W, ppool);
Andrew Scull80871322018-08-06 12:04:09 +0100262 if (!fdt) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100263 dlog("Unable to map FDT in r/w mode.\n");
264 goto err_unmap_fdt_header;
265 }
266
267 if (!fdt_find_child(&n, "")) {
268 dlog("Unable to find FDT root node.\n");
269 goto out_unmap_fdt;
270 }
271
272 if (!fdt_find_child(&n, "chosen")) {
273 dlog("Unable to find 'chosen'\n");
274 goto out_unmap_fdt;
275 }
276
277 /* Patch FDT to point to new ramdisk. */
Andrew Scull265ada92018-07-30 15:19:01 +0100278 if (!fdt_write_number(&n, "linux,initrd-start",
279 pa_addr(p->initrd_begin))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100280 dlog("Unable to write linux,initrd-start\n");
281 goto out_unmap_fdt;
282 }
283
Andrew Scull265ada92018-07-30 15:19:01 +0100284 if (!fdt_write_number(&n, "linux,initrd-end", pa_addr(p->initrd_end))) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100285 dlog("Unable to write linux,initrd-end\n");
286 goto out_unmap_fdt;
287 }
288
289 /* Patch fdt to reserve primary VM memory. */
Andrew Scull5991ec92018-10-08 14:55:02 +0100290 fdt_add_mem_reservation(fdt, pa_addr(layout_primary_begin()) & ~0xfffff,
291 0x80000);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100292
293 /* Patch fdt to reserve memory for secondary VMs. */
Andrew Walbran34ce72e2018-09-13 16:47:44 +0100294 for (i = 0; i < p->reserved_ranges_count; ++i) {
295 fdt_add_mem_reservation(
296 fdt, pa_addr(p->reserved_ranges[i].begin),
297 pa_addr(p->reserved_ranges[i].end) -
298 pa_addr(p->reserved_ranges[i].begin));
299 }
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100300
301 ret = true;
302
303out_unmap_fdt:
304 /* Unmap FDT. */
Andrew Scull80871322018-08-06 12:04:09 +0100305 if (!mm_unmap(fdt_addr,
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000306 pa_add(fdt_addr, fdt_total_size(fdt) + PAGE_SIZE), 0,
307 ppool)) {
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100308 dlog("Unable to unmap writable FDT.\n");
309 return false;
310 }
311 return ret;
312
313err_unmap_fdt_header:
Wedson Almeida Filho22d5eaa2018-12-16 00:38:49 +0000314 mm_unmap(fdt_addr, pa_add(fdt_addr, fdt_header_size()), 0, ppool);
Wedson Almeida Filhofdf4afc2018-07-19 15:45:21 +0100315 return false;
316}