1 /*
2 * Copyright 2018 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_handler.h"
10
11 #include "hf/check.h"
12 #include "hf/cpu.h"
13 #include "hf/dlog.h"
14 #include "hf/fdt.h"
15 #include "hf/mm.h"
16 #include "hf/std.h"
17
18 /**
19 * Initializes the FDT struct with the pointer to the FDT data (header) in
20 * fdt_ptr.
21 */
fdt_struct_from_ptr(const void * fdt_ptr,struct fdt * fdt)22 bool fdt_struct_from_ptr(const void *fdt_ptr, struct fdt *fdt)
23 {
24 size_t fdt_size;
25
26 if (!fdt_ptr || !fdt) {
27 return false;
28 }
29
30 return fdt_size_from_header(fdt_ptr, &fdt_size) &&
31 fdt_init_from_ptr(fdt, fdt_ptr, fdt_size);
32 }
33
34 /**
35 * Finds the memory region where initrd is stored.
36 */
fdt_find_initrd(const struct fdt * fdt,paddr_t * begin,paddr_t * end)37 bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end)
38 {
39 struct fdt_node n;
40 uint64_t initrd_begin;
41 uint64_t initrd_end;
42
43 if (!fdt_find_node(fdt, "/chosen", &n)) {
44 dlog_error("Unable to find '/chosen'\n");
45 return false;
46 }
47
48 if (!fdt_read_number(&n, FDT_PROP_INITRD_START, &initrd_begin)) {
49 dlog_error("Unable to read " FDT_PROP_INITRD_START "\n");
50 return false;
51 }
52
53 if (!fdt_read_number(&n, FDT_PROP_INITRD_END, &initrd_end)) {
54 dlog_error("Unable to read " FDT_PROP_INITRD_END "\n");
55 return false;
56 }
57
58 *begin = pa_init(initrd_begin);
59 *end = pa_init(initrd_end);
60
61 return true;
62 }
63
fdt_find_cpus(const struct fdt * fdt,cpu_id_t * cpu_ids,size_t * cpu_count)64 bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count)
65 {
66 static const struct string str_cpu = STRING_INIT("cpu");
67 struct fdt_node n;
68 size_t addr_size;
69
70 *cpu_count = 0;
71
72 if (!fdt_find_node(fdt, "/cpus", &n)) {
73 dlog_error("Unable to find '/cpus'\n");
74 return false;
75 }
76
77 if (!fdt_address_size(&n, &addr_size)) {
78 return false;
79 }
80
81 if (!fdt_first_child(&n)) {
82 return false;
83 }
84
85 do {
86 struct memiter data;
87
88 if (!fdt_read_property(&n, "device_type", &data) ||
89 !string_eq(&str_cpu, &data) ||
90 !fdt_read_property(&n, "reg", &data)) {
91 continue;
92 }
93
94 /* Get all entries for this CPU. */
95 while (memiter_size(&data)) {
96 uint64_t value;
97
98 if (*cpu_count >= MAX_CPUS) {
99 dlog_error("Found more than %d CPUs\n",
100 MAX_CPUS);
101 return false;
102 }
103
104 if (!fdt_parse_number(&data, addr_size, &value)) {
105 dlog_error("Could not parse CPU id\n");
106 return false;
107 }
108 cpu_ids[(*cpu_count)++] = value;
109 }
110 } while (fdt_next_sibling(&n));
111
112 return true;
113 }
114
fdt_find_memory_ranges(const struct fdt * fdt,const struct string * device_type,struct mem_range * mem_ranges,size_t * mem_ranges_count,size_t mem_range_limit)115 bool fdt_find_memory_ranges(const struct fdt *fdt,
116 const struct string *device_type,
117 struct mem_range *mem_ranges,
118 size_t *mem_ranges_count, size_t mem_range_limit)
119 {
120 struct fdt_node n;
121 size_t addr_size;
122 size_t size_size;
123 size_t mem_range_index = 0;
124
125 if (!fdt_find_node(fdt, "/", &n) || !fdt_address_size(&n, &addr_size) ||
126 !fdt_size_size(&n, &size_size)) {
127 return false;
128 }
129
130 /* Look for nodes with the device_type set to `device_type`. */
131 if (!fdt_first_child(&n)) {
132 return false;
133 }
134
135 do {
136 struct memiter data;
137
138 if (!fdt_read_property(&n, "device_type", &data) ||
139 !string_eq(device_type, &data) ||
140 !fdt_read_property(&n, "reg", &data)) {
141 continue;
142 }
143
144 /* Traverse all memory ranges within this node. */
145 while (memiter_size(&data)) {
146 uintpaddr_t addr;
147 size_t len;
148
149 CHECK(fdt_parse_number(&data, addr_size, &addr));
150 CHECK(fdt_parse_number(&data, size_size, &len));
151
152 if (mem_range_index < mem_range_limit) {
153 mem_ranges[mem_range_index].begin =
154 pa_init(addr);
155 mem_ranges[mem_range_index].end =
156 pa_init(addr + len);
157 ++mem_range_index;
158 } else {
159 dlog_error(
160 "Found %s range %u in FDT but only %u "
161 "supported, ignoring additional range "
162 "of size %u.\n",
163 string_data(device_type),
164 mem_range_index, mem_range_limit, len);
165 }
166 }
167 } while (fdt_next_sibling(&n));
168 *mem_ranges_count = mem_range_index;
169
170 return true;
171 }
172
fdt_map(struct fdt * fdt,struct mm_stage1_locked stage1_locked,paddr_t fdt_addr,struct mpool * ppool)173 bool fdt_map(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
174 paddr_t fdt_addr, struct mpool *ppool)
175 {
176 const void *fdt_ptr;
177 size_t fdt_len;
178
179 /* Map the fdt header in. */
180 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
181 pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
182 MM_MODE_R, ppool);
183 if (!fdt_ptr) {
184 dlog_error("Unable to map FDT header.\n");
185 return NULL;
186 }
187
188 if (!fdt_size_from_header(fdt_ptr, &fdt_len)) {
189 dlog_error("FDT failed header validation.\n");
190 goto fail;
191 }
192
193 /* Map the rest of the fdt in. */
194 fdt_ptr = mm_identity_map(stage1_locked, fdt_addr,
195 pa_add(fdt_addr, fdt_len), MM_MODE_R, ppool);
196 if (!fdt_ptr) {
197 dlog_error("Unable to map full FDT.\n");
198 goto fail;
199 }
200
201 if (!fdt_init_from_ptr(fdt, fdt_ptr, fdt_len)) {
202 dlog_error("FDT failed validation.\n");
203 goto fail_full;
204 }
205
206 return true;
207
208 fail_full:
209 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_len), ppool);
210 return false;
211
212 fail:
213 mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, FDT_V17_HEADER_SIZE),
214 ppool);
215 return false;
216 }
217
fdt_unmap(struct fdt * fdt,struct mm_stage1_locked stage1_locked,struct mpool * ppool)218 bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
219 struct mpool *ppool)
220 {
221 paddr_t begin = pa_from_va(va_from_ptr(fdt_base(fdt)));
222 paddr_t end = pa_add(begin, fdt_size(fdt));
223
224 if (!mm_unmap(stage1_locked, begin, end, ppool)) {
225 return false;
226 }
227
228 /* Invalidate pointer to the buffer. */
229 fdt_fini(fdt);
230 return true;
231 }
232
233 /**
234 * Gets the size of the first memory range from the FDT into size.
235 *
236 * The test framework expects the address space to be contiguous, therefore
237 * gets the size of the first memory range, if there is more than one range.
238 */
fdt_get_memory_size(const struct fdt * fdt,size_t * size)239 bool fdt_get_memory_size(const struct fdt *fdt, size_t *size)
240 {
241 const struct string memory_device_type = STRING_INIT("memory");
242 struct mem_range mem_range;
243 size_t mem_ranges_count;
244
245 if (!fdt || !size ||
246 !fdt_find_memory_ranges(fdt, &memory_device_type, &mem_range,
247 &mem_ranges_count, 1)) {
248 return false;
249 }
250
251 if (mem_ranges_count < 1) {
252 return false;
253 }
254
255 *size = pa_difference(mem_range.begin, mem_range.end);
256
257 return true;
258 }
259