blob: 81202e26f899570eb451c14b24a20c6bcb792bf9 [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
Andrew Walbran309ff342020-04-15 15:55:05 +010098 /* Allow some extra room for the modifications to the FDT. */
99 if (fdt_open_into(fdt, fdt, buf_size) != 0) {
100 dlog_error("FDT failed to open_into.\n");
101 goto out_unmap_fdt;
102 }
103
David Brazdilb856be62020-03-25 10:14:55 +0000104 off = fdt_path_offset(fdt, "/chosen");
105 if (off < 0) {
106 dlog_error("Unable to find FDT '/chosen' node.\n");
107 goto out_unmap_fdt;
108 }
109
110 /* Patch FDT to point to new ramdisk. */
111 if (!patch_uint(fdt, off, FDT_PROP_INITRD_START,
112 pa_addr(p->initrd_begin))) {
113 dlog_error("Unable to write" FDT_PROP_INITRD_START "\n");
114 goto out_unmap_fdt;
115 }
116
117 if (!patch_uint(fdt, off, FDT_PROP_INITRD_END,
118 pa_addr(p->initrd_end))) {
119 dlog_error("Unable to write " FDT_PROP_INITRD_END "\n");
120 goto out_unmap_fdt;
121 }
122
123 /*
124 * Patch FDT to reserve hypervisor memory so the primary VM doesn't try
125 * to use it.
126 */
127 rsv = true;
128 rsv &= add_mem_reservation(fdt, layout_text_begin(), layout_text_end());
129 rsv &= add_mem_reservation(fdt, layout_rodata_begin(),
130 layout_rodata_end());
131 rsv &= add_mem_reservation(fdt, layout_data_begin(), layout_data_end());
132
133 /* Patch FDT to reserve memory for secondary VMs. */
134 for (i = 0; i < p->reserved_ranges_count; ++i) {
135 struct mem_range range = p->reserved_ranges[i];
136
137 rsv &= add_mem_reservation(fdt, range.begin, range.end);
138 }
139
140 if (!rsv) {
141 dlog_error("Unable to add memory reservations to FDT.\n");
142 goto out_unmap_fdt;
143 }
144
Andrew Walbran309ff342020-04-15 15:55:05 +0100145 if (fdt_pack(fdt) != 0) {
146 dlog_error("Failed to pack FDT.\n");
147 goto out_unmap_fdt;
148 }
149
David Brazdilb856be62020-03-25 10:14:55 +0000150 ret = true;
151
152out_unmap_fdt:
153 /* Unmap FDT. */
154 if (!mm_unmap(stage1_locked, fdt_addr,
155 pa_add(fdt_addr, fdt_totalsize(fdt) + PAGE_SIZE),
156 ppool)) {
157 dlog_error("Unable to unmap writable FDT.\n");
158 return false;
159 }
160 return ret;
161
162err_unmap_fdt_header:
163 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
164 ppool);
165 return false;
166}