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