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