blob: 439a79f050cac0e94a9b09a7f7699242c2bd6cd2 [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;
55 size_t 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
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}