blob: 5161192f677ace127a63bb2f10f85e2d7c022dfb [file] [log] [blame]
David Brazdilb856be62020-03-25 10:14:55 +00001/*
2 * Copyright 2020 The Hafnium Authors.
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
17#include "hf/fdt_patch.h"
18
19#include <libfdt.h>
20
21#include "hf/boot_params.h"
22#include "hf/dlog.h"
23#include "hf/fdt.h"
24#include "hf/fdt_handler.h"
25#include "hf/layout.h"
26#include "hf/mm.h"
27
28static bool patch_uint(void *fdt, int off, const char *prop, uint64_t val)
29{
30 const void *data;
31 int lenp;
32
33 data = fdt_getprop(fdt, off, prop, &lenp);
34 if (data == NULL) {
35 return false;
36 }
37
38 switch (lenp) {
39 case sizeof(uint64_t): {
40 return fdt_setprop_inplace_u64(fdt, off, prop, val) == 0;
41 }
42 case sizeof(uint32_t): {
43 return (val <= UINT32_MAX) &&
44 (fdt_setprop_inplace_u32(fdt, off, prop, val) == 0);
45 }
46 default: {
47 return false;
48 }
49 }
50}
51
52static bool add_mem_reservation(void *fdt, paddr_t begin, paddr_t end)
53{
54 size_t len = pa_difference(begin, end);
55
56 return fdt_add_mem_rsv(fdt, pa_addr(begin), len) == 0;
57}
58
59bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
60 struct boot_params_update *p, struct mpool *ppool)
61{
62 void *fdt;
63 size_t buf_size;
64 int off;
65 bool ret = false;
66 bool rsv;
67 size_t i;
68
69 /* Map the fdt header in. */
70 fdt = mm_identity_map(stage1_locked, fdt_addr,
71 pa_add(fdt_addr, FDT_V17_HEADER_SIZE), MM_MODE_R,
72 ppool);
73 if (!fdt) {
74 dlog_error("Unable to map FDT header.\n");
75 return false;
76 }
77
78 if (fdt_check_header(fdt) != 0) {
79 dlog_error("FDT failed header validation.\n");
80 goto err_unmap_fdt_header;
81 }
82
83 /* Map the fdt (+ a page) in r/w mode in preparation for updating it. */
84 buf_size = fdt_totalsize(fdt) + PAGE_SIZE;
85 fdt = mm_identity_map(stage1_locked, fdt_addr,
86 pa_add(fdt_addr, buf_size), MM_MODE_R | MM_MODE_W,
87 ppool);
88 if (!fdt) {
89 dlog_error("Unable to map FDT in r/w mode.\n");
90 goto err_unmap_fdt_header;
91 }
92
93 if (fdt_check_full(fdt, buf_size) != 0) {
94 dlog_error("FDT failed validation.\n");
95 goto out_unmap_fdt;
96 }
97
98 off = fdt_path_offset(fdt, "/chosen");
99 if (off < 0) {
100 dlog_error("Unable to find FDT '/chosen' node.\n");
101 goto out_unmap_fdt;
102 }
103
104 /* Patch FDT to point to new ramdisk. */
105 if (!patch_uint(fdt, off, FDT_PROP_INITRD_START,
106 pa_addr(p->initrd_begin))) {
107 dlog_error("Unable to write" FDT_PROP_INITRD_START "\n");
108 goto out_unmap_fdt;
109 }
110
111 if (!patch_uint(fdt, off, FDT_PROP_INITRD_END,
112 pa_addr(p->initrd_end))) {
113 dlog_error("Unable to write " FDT_PROP_INITRD_END "\n");
114 goto out_unmap_fdt;
115 }
116
117 /*
118 * Patch FDT to reserve hypervisor memory so the primary VM doesn't try
119 * to use it.
120 */
121 rsv = true;
122 rsv &= add_mem_reservation(fdt, layout_text_begin(), layout_text_end());
123 rsv &= add_mem_reservation(fdt, layout_rodata_begin(),
124 layout_rodata_end());
125 rsv &= add_mem_reservation(fdt, layout_data_begin(), layout_data_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 ret = true;
140
141out_unmap_fdt:
142 /* Unmap FDT. */
143 if (!mm_unmap(stage1_locked, fdt_addr,
144 pa_add(fdt_addr, fdt_totalsize(fdt) + PAGE_SIZE),
145 ppool)) {
146 dlog_error("Unable to unmap writable FDT.\n");
147 return false;
148 }
149 return ret;
150
151err_unmap_fdt_header:
152 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
153 ppool);
154 return false;
155}