blob: 4e7f5357423e39bccb970775e34e3806a9956207 [file] [log] [blame]
David Brazdilb856be62020-03-25 10:14:55 +00001/*
2 * Copyright 2020 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
David Brazdilb856be62020-03-25 10:14:55 +00007 */
8
9#include "hf/fdt_patch.h"
10
11#include <libfdt.h>
12
13#include "hf/boot_params.h"
14#include "hf/dlog.h"
15#include "hf/fdt.h"
16#include "hf/fdt_handler.h"
17#include "hf/layout.h"
18#include "hf/mm.h"
19
20static bool patch_uint(void *fdt, int off, const char *prop, uint64_t val)
21{
22 const void *data;
23 int lenp;
24
25 data = fdt_getprop(fdt, off, prop, &lenp);
26 if (data == NULL) {
27 return false;
28 }
29
30 switch (lenp) {
31 case sizeof(uint64_t): {
32 return fdt_setprop_inplace_u64(fdt, off, prop, val) == 0;
33 }
34 case sizeof(uint32_t): {
35 return (val <= UINT32_MAX) &&
36 (fdt_setprop_inplace_u32(fdt, off, prop, val) == 0);
37 }
38 default: {
39 return false;
40 }
41 }
42}
43
44static bool add_mem_reservation(void *fdt, paddr_t begin, paddr_t end)
45{
46 size_t len = pa_difference(begin, end);
47
48 return fdt_add_mem_rsv(fdt, pa_addr(begin), len) == 0;
49}
50
51bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
52 struct boot_params_update *p, struct mpool *ppool)
53{
54 void *fdt;
Daniel Boulby4dd3f532021-09-21 09:57:08 +010055 int buf_size;
David Brazdilb856be62020-03-25 10:14:55 +000056 int off;
57 bool ret = false;
58 bool rsv;
59 size_t i;
60
61 /* Map the fdt header in. */
62 fdt = mm_identity_map(stage1_locked, fdt_addr,
63 pa_add(fdt_addr, FDT_V17_HEADER_SIZE), MM_MODE_R,
64 ppool);
65 if (!fdt) {
66 dlog_error("Unable to map FDT header.\n");
67 return false;
68 }
69
70 if (fdt_check_header(fdt) != 0) {
71 dlog_error("FDT failed header validation.\n");
72 goto err_unmap_fdt_header;
73 }
74
75 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
76 buf_size = fdt_totalsize(fdt) + PAGE_SIZE;
77 fdt = mm_identity_map(stage1_locked, fdt_addr,
78 pa_add(fdt_addr, buf_size), MM_MODE_R | MM_MODE_W,
79 ppool);
80 if (!fdt) {
81 dlog_error("Unable to map FDT in r/w mode.\n");
82 goto err_unmap_fdt_header;
83 }
84
85 if (fdt_check_full(fdt, buf_size) != 0) {
86 dlog_error("FDT failed validation.\n");
87 goto out_unmap_fdt;
88 }
89
Andrew Walbran309ff342020-04-15 15:55:05 +010090 /* Allow some extra room for the modifications to the FDT. */
91 if (fdt_open_into(fdt, fdt, buf_size) != 0) {
92 dlog_error("FDT failed to open_into.\n");
93 goto out_unmap_fdt;
94 }
95
David Brazdilb856be62020-03-25 10:14:55 +000096 off = fdt_path_offset(fdt, "/chosen");
97 if (off < 0) {
98 dlog_error("Unable to find FDT '/chosen' node.\n");
99 goto out_unmap_fdt;
100 }
101
102 /* Patch FDT to point to new ramdisk. */
103 if (!patch_uint(fdt, off, FDT_PROP_INITRD_START,
104 pa_addr(p->initrd_begin))) {
105 dlog_error("Unable to write" FDT_PROP_INITRD_START "\n");
106 goto out_unmap_fdt;
107 }
108
109 if (!patch_uint(fdt, off, FDT_PROP_INITRD_END,
110 pa_addr(p->initrd_end))) {
111 dlog_error("Unable to write " FDT_PROP_INITRD_END "\n");
112 goto out_unmap_fdt;
113 }
114
115 /*
116 * Patch FDT to reserve hypervisor memory so the primary VM doesn't try
117 * to use it.
118 */
119 rsv = true;
120 rsv &= add_mem_reservation(fdt, layout_text_begin(), layout_text_end());
121 rsv &= add_mem_reservation(fdt, layout_rodata_begin(),
122 layout_rodata_end());
123 rsv &= add_mem_reservation(fdt, layout_data_begin(), layout_data_end());
124
125 /* Patch FDT to reserve memory for secondary VMs. */
126 for (i = 0; i < p->reserved_ranges_count; ++i) {
127 struct mem_range range = p->reserved_ranges[i];
128
129 rsv &= add_mem_reservation(fdt, range.begin, range.end);
130 }
131
132 if (!rsv) {
133 dlog_error("Unable to add memory reservations to FDT.\n");
134 goto out_unmap_fdt;
135 }
136
Andrew Walbran309ff342020-04-15 15:55:05 +0100137 if (fdt_pack(fdt) != 0) {
138 dlog_error("Failed to pack FDT.\n");
139 goto out_unmap_fdt;
140 }
141
David Brazdilb856be62020-03-25 10:14:55 +0000142 ret = true;
143
144out_unmap_fdt:
145 /* Unmap FDT. */
146 if (!mm_unmap(stage1_locked, fdt_addr,
147 pa_add(fdt_addr, fdt_totalsize(fdt) + PAGE_SIZE),
148 ppool)) {
149 dlog_error("Unable to unmap writable FDT.\n");
150 return false;
151 }
152 return ret;
153
154err_unmap_fdt_header:
155 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
156 ppool);
157 return false;
158}
Fuad Tabba50469e02020-06-30 15:14:28 +0100159
160bool fdt_patch_mem(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
161 size_t fdt_max_size, paddr_t mem_begin, paddr_t mem_end,
162 struct mpool *ppool)
163{
164 int ret = 0;
165 uint64_t mem_start_addr = pa_addr(mem_begin);
166 size_t mem_size = pa_difference(mem_begin, mem_end);
167 struct fdt_header *fdt;
168 int fdt_memory_node;
169 int root_offset;
170
171 /* Map the fdt in r/w mode in preparation for updating it. */
172 fdt = mm_identity_map(stage1_locked, fdt_addr,
173 pa_add(fdt_addr, fdt_max_size),
174 MM_MODE_R | MM_MODE_W, ppool);
175
176 if (!fdt) {
177 dlog_error("Unable to map FDT in r/w mode.\n");
178 return false;
179 }
180
181 ret = fdt_check_full(fdt, fdt_max_size);
182 if (ret != 0) {
183 dlog_error("FDT failed validation. Error: %d\n", ret);
184 goto out_unmap_fdt;
185 }
186
187 /* Allow some extra room for patches to the FDT. */
Daniel Boulby4dd3f532021-09-21 09:57:08 +0100188 ret = fdt_open_into(fdt, fdt, (int)fdt_max_size);
Fuad Tabba50469e02020-06-30 15:14:28 +0100189 if (ret != 0) {
190 dlog_error("FDT failed to open_into. Error: %d\n", ret);
191 goto out_unmap_fdt;
192 }
193
194 root_offset = fdt_path_offset(fdt, "/");
195 if (ret < 0) {
196 dlog_error("FDT cannot find root offset. Error: %d\n", ret);
197 goto out_unmap_fdt;
198 }
199
200 /* Add a node to hold the memory information. */
201 fdt_memory_node = fdt_add_subnode(fdt, root_offset, "memory");
202 if (fdt_memory_node < 0) {
203 ret = fdt_memory_node;
204 dlog_error("FDT cannot add memory node. Error: %d\n", ret);
205 goto out_unmap_fdt;
206 }
207
208 /* Set the values for the VM's memory in the FDT. */
209 ret = fdt_appendprop_addrrange(fdt, root_offset, fdt_memory_node, "reg",
210 mem_start_addr, mem_size);
211 if (ret != 0) {
212 dlog_error(
213 "FDT failed to append address range property for "
214 "memory. Error: %d\n",
215 ret);
216 goto out_unmap_fdt;
217 }
218
219 ret = fdt_appendprop_string(fdt, fdt_memory_node, "device_type",
220 "memory");
221 if (ret != 0) {
222 dlog_error(
223 "FDT failed to append device_type property for memory. "
224 "Error: %d\n",
225 ret);
226 goto out_unmap_fdt;
227 }
228
229 ret = fdt_pack(fdt);
230 if (ret != 0) {
231 dlog_error("Failed to pack FDT. Error: %d\n", ret);
232 goto out_unmap_fdt;
233 }
234
235out_unmap_fdt:
236 /* Unmap FDT. */
237 if (!mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_max_size),
238 ppool)) {
239 dlog_error("Unable to unmap writable FDT.\n");
240 return false;
241 }
242
243 return ret == 0;
244}