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.h"
10 
11 #include <libfdt.h>
12 
13 #include "hf/static_assert.h"
14 
15 /** Returns pointer to the FDT buffer. */
fdt_base(const struct fdt * fdt)16 const void *fdt_base(const struct fdt *fdt)
17 {
18 	return memiter_base(&fdt->buf);
19 }
20 
21 /** Returns size of the FDT buffer. */
fdt_size(const struct fdt * fdt)22 size_t fdt_size(const struct fdt *fdt)
23 {
24 	return memiter_size(&fdt->buf);
25 }
26 
27 /**
28  * Extracts total size of the FDT structure from its FDT header.
29  * Returns true on success, false if header validation failed.
30  */
fdt_size_from_header(const void * ptr,size_t * val)31 bool fdt_size_from_header(const void *ptr, size_t *val)
32 {
33 	if (fdt_check_header(ptr) != 0) {
34 		return false;
35 	}
36 
37 	*val = fdt_totalsize(ptr);
38 	return true;
39 }
40 
41 /**
42  * Initializes `struct fdt` to point to a given buffer.
43  * Returns true on success, false if FDT validation failed.
44  */
fdt_init_from_ptr(struct fdt * fdt,const void * ptr,size_t len)45 bool fdt_init_from_ptr(struct fdt *fdt, const void *ptr, size_t len)
46 {
47 	if (fdt_check_full(ptr, len) != 0) {
48 		return false;
49 	}
50 
51 	memiter_init(&fdt->buf, ptr, len);
52 	return true;
53 }
54 
55 /**
56  * Initializes `struct fdt` to point to a given buffer.
57  * Returns true on success, false if FDT validation failed.
58  */
fdt_init_from_memiter(struct fdt * fdt,const struct memiter * it)59 bool fdt_init_from_memiter(struct fdt *fdt, const struct memiter *it)
60 {
61 	return fdt_init_from_ptr(fdt, memiter_base(it), memiter_size(it));
62 }
63 
64 /**
65  * Invalidates the internal pointer to FDT buffer.
66  * This is meant to prevent use-after-free bugs.
67  */
fdt_fini(struct fdt * fdt)68 void fdt_fini(struct fdt *fdt)
69 {
70 	memiter_init(&fdt->buf, NULL, 0);
71 }
72 
73 /**
74  * Finds a node of a given path in the device tree.
75  * Unit addresses of components may be omitted but result is undefined if
76  * the path is not unique.
77  * Returns true on success, false if not found or an error occurred.
78  */
fdt_find_node(const struct fdt * fdt,const char * path,struct fdt_node * node)79 bool fdt_find_node(const struct fdt *fdt, const char *path,
80 		   struct fdt_node *node)
81 {
82 	int offset = fdt_path_offset(fdt_base(fdt), path);
83 
84 	if (offset < 0) {
85 		return false;
86 	}
87 
88 	*node = (struct fdt_node){.fdt = *fdt, .offset = offset};
89 	return true;
90 }
91 
92 /**
93  * Retrieves address size for a bus represented in the device tree.
94  * Result is value of '#address-cells' at `node` multiplied by cell size.
95  * If '#address-cells' is not found, the default value is 2 cells.
96  * Returns true on success, false if an error occurred.
97  */
fdt_address_size(const struct fdt_node * node,size_t * size)98 bool fdt_address_size(const struct fdt_node *node, size_t *size)
99 {
100 	int s = fdt_address_cells(fdt_base(&node->fdt), node->offset);
101 
102 	if (s < 0) {
103 		return false;
104 	}
105 
106 	*size = (size_t)s * sizeof(uint32_t);
107 	return true;
108 }
109 
110 /**
111  * Retrieves address range size for a bus represented in the device tree.
112  * Result is value of '#size-cells' at `node` multiplied by cell size.
113  * If '#size-cells' is not found, the default value is 1 cell.
114  * Returns true on success, false if an error occurred.
115  */
fdt_size_size(const struct fdt_node * node,size_t * size)116 bool fdt_size_size(const struct fdt_node *node, size_t *size)
117 {
118 	int s = fdt_size_cells(fdt_base(&node->fdt), node->offset);
119 
120 	if (s < 0) {
121 		return false;
122 	}
123 
124 	*size = (size_t)s * sizeof(uint32_t);
125 	return true;
126 }
127 
128 /**
129  * Retrieves the buffer with value of property `name` at `node`.
130  * Returns true on success, false if not found or an error occurred.
131  */
fdt_read_property(const struct fdt_node * node,const char * name,struct memiter * data)132 bool fdt_read_property(const struct fdt_node *node, const char *name,
133 		       struct memiter *data)
134 {
135 	const void *ptr;
136 	int lenp;
137 
138 	ptr = fdt_getprop(fdt_base(&node->fdt), node->offset, name, &lenp);
139 	if (ptr == NULL) {
140 		return false;
141 	}
142 
143 	CHECK(lenp >= 0);
144 	memiter_init(data, ptr, (size_t)lenp);
145 	return true;
146 }
147 
148 /**
149  * Reads the value of property `name` at `node` as a uint.
150  * The size of the uint is inferred from the size of the property's value.
151  * Returns true on success, false if property not found or an error occurred.
152  */
fdt_read_number(const struct fdt_node * node,const char * name,uint64_t * val)153 bool fdt_read_number(const struct fdt_node *node, const char *name,
154 		     uint64_t *val)
155 {
156 	struct memiter data;
157 
158 	return fdt_read_property(node, name, &data) &&
159 	       fdt_parse_number(&data, memiter_size(&data), val) &&
160 	       (memiter_size(&data) == 0);
161 }
162 
163 /**
164  * Parses a uint of given `size` from the beginning of `data`.
165  * On success returns true and advances `data` by `size` bytes.
166  * Returns false if `data` is too short or uints of `size` are not supported.
167  */
fdt_parse_number(struct memiter * data,size_t size,uint64_t * val)168 bool fdt_parse_number(struct memiter *data, size_t size, uint64_t *val)
169 {
170 	struct memiter data_int;
171 	struct memiter data_rem;
172 
173 	data_rem = *data;
174 	if (!memiter_consume(&data_rem, size, &data_int)) {
175 		return false;
176 	}
177 
178 	switch (size) {
179 	case sizeof(uint32_t): {
180 		static_assert(sizeof(uint32_t) == sizeof(fdt32_t),
181 			      "Size mismatch");
182 		*val = fdt32_ld((const fdt32_t *)memiter_base(&data_int));
183 		break;
184 	}
185 	case sizeof(uint64_t): {
186 		static_assert(sizeof(uint64_t) == sizeof(fdt64_t),
187 			      "Size mismatch");
188 		*val = fdt64_ld((const fdt64_t *)memiter_base(&data_int));
189 		break;
190 	}
191 	default: {
192 		return false;
193 	}
194 	}
195 
196 	*data = data_rem;
197 	return true;
198 }
199 
200 /**
201  * Finds first direct subnode of `node`.
202  * If found, makes `node` point to the subnode and returns true.
203  * Returns false if no subnode is found.
204  */
fdt_first_child(struct fdt_node * node)205 bool fdt_first_child(struct fdt_node *node)
206 {
207 	int child_off = fdt_first_subnode(fdt_base(&node->fdt), node->offset);
208 
209 	if (child_off < 0) {
210 		return false;
211 	}
212 
213 	node->offset = child_off;
214 	return true;
215 }
216 
217 /**
218  * Finds next sibling node of `node`. Call repeatedly to discover all siblings.
219  * If found, makes `node` point to the next sibling node and returns true.
220  * Returns false if no next sibling node is found.
221  */
fdt_next_sibling(struct fdt_node * node)222 bool fdt_next_sibling(struct fdt_node *node)
223 {
224 	int sib_off = fdt_next_subnode(fdt_base(&node->fdt), node->offset);
225 
226 	if (sib_off < 0) {
227 		return false;
228 	}
229 
230 	node->offset = sib_off;
231 	return true;
232 }
233 
234 /**
235  * Finds a node named `name` among subnodes of `node`.
236  * Returns true if found, false if not found or an error occurred.
237  */
fdt_find_child(struct fdt_node * node,const struct string * name)238 bool fdt_find_child(struct fdt_node *node, const struct string *name)
239 {
240 	struct fdt_node child = *node;
241 	const void *base = fdt_base(&node->fdt);
242 
243 	if (!fdt_first_child(&child)) {
244 		return false;
245 	}
246 
247 	do {
248 		const char *child_name;
249 		int lenp;
250 		struct memiter it;
251 
252 		child_name = fdt_get_name(base, child.offset, &lenp);
253 		if (child_name == NULL) {
254 			/* Error */
255 			return false;
256 		}
257 
258 		CHECK(lenp >= 0);
259 		memiter_init(&it, child_name, (size_t)lenp);
260 		if (string_eq(name, &it)) {
261 			node->offset = child.offset;
262 			return true;
263 		}
264 	} while (fdt_next_sibling(&child));
265 
266 	/* Not found */
267 	return false;
268 }
269 
270 /**
271  * Returns true if `node` has property "compatible" containing a `compat` entry.
272  * Returns false if node not compatible or an error occurred.
273  */
fdt_is_compatible(struct fdt_node * node,const char * compat)274 bool fdt_is_compatible(struct fdt_node *node, const char *compat)
275 {
276 	return fdt_node_check_compatible(fdt_base(&node->fdt), node->offset,
277 					 compat) == 0;
278 }
279