1 /*
2 * Copyright 2020 The Hafnium Authors.
3 *
4 * 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.
7 */
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
patch_uint(void * fdt,int off,const char * prop,uint64_t val)20 static 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
add_mem_reservation(void * fdt,paddr_t begin,paddr_t end)44 static 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
fdt_patch(struct mm_stage1_locked stage1_locked,paddr_t fdt_addr,struct boot_params_update * p,struct mpool * ppool)51 bool 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;
55 int buf_size;
56 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
90 /* 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
96 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 rsv &= add_mem_reservation(fdt, layout_stacks_begin(),
125 layout_stacks_end());
126
127 /* Patch FDT to reserve memory for secondary VMs. */
128 for (i = 0; i < p->reserved_ranges_count; ++i) {
129 struct mem_range range = p->reserved_ranges[i];
130
131 rsv &= add_mem_reservation(fdt, range.begin, range.end);
132 }
133
134 if (!rsv) {
135 dlog_error("Unable to add memory reservations to FDT.\n");
136 goto out_unmap_fdt;
137 }
138
139 if (fdt_pack(fdt) != 0) {
140 dlog_error("Failed to pack FDT.\n");
141 goto out_unmap_fdt;
142 }
143
144 ret = true;
145
146 out_unmap_fdt:
147 /* Unmap FDT. */
148 if (!mm_unmap(stage1_locked, fdt_addr,
149 pa_add(fdt_addr, fdt_totalsize(fdt) + PAGE_SIZE),
150 ppool)) {
151 dlog_error("Unable to unmap writable FDT.\n");
152 return false;
153 }
154 return ret;
155
156 err_unmap_fdt_header:
157 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
158 ppool);
159 return false;
160 }
161
fdt_patch_mem(struct mm_stage1_locked stage1_locked,paddr_t fdt_addr,size_t fdt_max_size,paddr_t mem_begin,paddr_t mem_end,struct mpool * ppool)162 bool fdt_patch_mem(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
163 size_t fdt_max_size, paddr_t mem_begin, paddr_t mem_end,
164 struct mpool *ppool)
165 {
166 int ret = 0;
167 uint64_t mem_start_addr = pa_addr(mem_begin);
168 size_t mem_size = pa_difference(mem_begin, mem_end);
169 struct fdt_header *fdt;
170 int fdt_memory_node;
171 int root_offset;
172
173 /* Map the fdt in r/w mode in preparation for updating it. */
174 fdt = mm_identity_map(stage1_locked, fdt_addr,
175 pa_add(fdt_addr, fdt_max_size),
176 MM_MODE_R | MM_MODE_W, ppool);
177
178 if (!fdt) {
179 dlog_error("Unable to map FDT in r/w mode.\n");
180 return false;
181 }
182
183 ret = fdt_check_full(fdt, fdt_max_size);
184 if (ret != 0) {
185 dlog_error("FDT failed validation. Error: %d\n", ret);
186 goto out_unmap_fdt;
187 }
188
189 /* Allow some extra room for patches to the FDT. */
190 ret = fdt_open_into(fdt, fdt, (int)fdt_max_size);
191 if (ret != 0) {
192 dlog_error("FDT failed to open_into. Error: %d\n", ret);
193 goto out_unmap_fdt;
194 }
195
196 root_offset = fdt_path_offset(fdt, "/");
197 if (ret < 0) {
198 dlog_error("FDT cannot find root offset. Error: %d\n", ret);
199 goto out_unmap_fdt;
200 }
201
202 /* Add a node to hold the memory information. */
203 fdt_memory_node = fdt_add_subnode(fdt, root_offset, "memory");
204 if (fdt_memory_node < 0) {
205 ret = fdt_memory_node;
206 dlog_error("FDT cannot add memory node. Error: %d\n", ret);
207 goto out_unmap_fdt;
208 }
209
210 /* Set the values for the VM's memory in the FDT. */
211 ret = fdt_appendprop_addrrange(fdt, root_offset, fdt_memory_node, "reg",
212 mem_start_addr, mem_size);
213 if (ret != 0) {
214 dlog_error(
215 "FDT failed to append address range property for "
216 "memory. Error: %d\n",
217 ret);
218 goto out_unmap_fdt;
219 }
220
221 ret = fdt_appendprop_string(fdt, fdt_memory_node, "device_type",
222 "memory");
223 if (ret != 0) {
224 dlog_error(
225 "FDT failed to append device_type property for memory. "
226 "Error: %d\n",
227 ret);
228 goto out_unmap_fdt;
229 }
230
231 ret = fdt_pack(fdt);
232 if (ret != 0) {
233 dlog_error("Failed to pack FDT. Error: %d\n", ret);
234 goto out_unmap_fdt;
235 }
236
237 out_unmap_fdt:
238 /* Unmap FDT. */
239 if (!mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_max_size),
240 ppool)) {
241 dlog_error("Unable to unmap writable FDT.\n");
242 return false;
243 }
244
245 return ret == 0;
246 }
247