]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/fdt/fdt_overlay.c
stand/fdt: Don't leak next_fdtp if we fail to open overlay
[FreeBSD/FreeBSD.git] / stand / fdt / fdt_overlay.c
1 /*-
2  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <stand.h>
31 #include <libfdt.h>
32
33 #include "fdt_overlay.h"
34
35 /*
36  * Get max phandle
37  */
38 static uint32_t
39 fdt_max_phandle(void *fdtp)
40 {
41         int o, depth;
42         uint32_t max_phandle, phandle;
43
44         depth = 1;
45         o = fdt_path_offset(fdtp, "/");
46         max_phandle = fdt_get_phandle(fdtp, o);
47         for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(fdtp, o, &depth)) {
48                 phandle = fdt_get_phandle(fdtp, o);
49                 if (max_phandle < phandle)
50                         max_phandle = phandle;
51         }
52
53         return max_phandle;
54 }
55
56 /*
57  * Returns exact memory location specified by fixup in format
58  * /path/to/node:property:offset
59  */
60 static void *
61 fdt_get_fixup_location(void *fdtp, const char *fixup)
62 {
63         char *path, *prop, *offsetp, *endp;
64         int prop_offset, o, proplen;
65         void  *result;
66
67         result = NULL;
68
69         path = strdup(fixup);
70         prop = strchr(path, ':');
71         if (prop == NULL) {
72                 printf("missing property part in \"%s\"\n", fixup);
73                 result = NULL;
74                 goto out;
75         }
76
77         *prop = 0;
78         prop++;
79
80         offsetp = strchr(prop, ':');
81         if (offsetp == NULL) {
82                 printf("missing offset part in \"%s\"\n", fixup);
83                 result = NULL;
84                 goto out;
85         }
86
87         *offsetp = 0;
88         offsetp++;
89
90         prop_offset = strtoul(offsetp, &endp, 10);
91         if (*endp != '\0') {
92                 printf("\"%s\" is not valid number\n", offsetp);
93                 result = NULL;
94                 goto out;
95         }
96
97         o = fdt_path_offset(fdtp, path);
98         if (o < 0) {
99                 printf("path \"%s\" not found\n", path);
100                 result = NULL;
101                 goto out;
102         }
103
104         result = fdt_getprop_w(fdtp, o, prop, &proplen);
105         if (result == NULL){
106                 printf("property \"%s\" not found in  \"%s\" node\n", prop, path);
107                 result = NULL;
108                 goto out;
109         }
110
111         if (proplen < prop_offset + sizeof(uint32_t)) {
112                 printf("%s: property length is too small for fixup\n", fixup);
113                 result = NULL;
114                 goto out;
115         }
116
117         result = (char*)result + prop_offset;
118
119 out:
120         free(path);
121         return (result);
122 }
123
124 /*
125  * Process one entry in __fixups__ { } node
126  * @fixups is property value, array of NUL-terminated strings
127  *   with fixup locations
128  * @fixups_len length of the fixups array in bytes
129  * @phandle is value for these locations
130  */
131 static int
132 fdt_do_one_fixup(void *fdtp, const char *fixups, int fixups_len, int phandle)
133 {
134         void *fixup_pos;
135         uint32_t val;
136
137         val = cpu_to_fdt32(phandle);
138
139         while (fixups_len > 0) {
140                 fixup_pos = fdt_get_fixup_location(fdtp, fixups);
141                 if (fixup_pos != NULL)
142                         memcpy(fixup_pos, &val, sizeof(val));
143
144                 fixups_len -= strlen(fixups) + 1;
145                 fixups += strlen(fixups) + 1;
146         }
147
148         return (0);
149 }
150
151 /*
152  * Increase u32 value at pos by offset
153  */
154 static void
155 fdt_increase_u32(void *pos, uint32_t offset)
156 {
157         uint32_t val;
158
159         memcpy(&val, pos,  sizeof(val));
160         val = cpu_to_fdt32(fdt32_to_cpu(val) + offset);
161         memcpy(pos, &val, sizeof(val));
162 }
163
164 /*
165  * Process local fixups
166  * @fixups is property value, array of NUL-terminated strings
167  *   with fixup locations
168  * @fixups_len length of the fixups array in bytes
169  * @offset value these locations should be increased by
170  */
171 static int
172 fdt_do_local_fixup(void *fdtp, const char *fixups, int fixups_len, int offset)
173 {
174         void *fixup_pos;
175
176         while (fixups_len > 0) {
177                 fixup_pos = fdt_get_fixup_location(fdtp, fixups);
178                 if (fixup_pos != NULL)
179                         fdt_increase_u32(fixup_pos, offset);
180
181                 fixups_len -= strlen(fixups) + 1;
182                 fixups += strlen(fixups) + 1;
183         }
184
185         return (0);
186 }
187
188 /*
189  * Increase node phandle by phandle_offset
190  */
191 static void
192 fdt_increase_phandle(void *fdtp, int node_offset, uint32_t phandle_offset)
193 {
194         int proplen;
195         void *phandle_pos, *node_pos;
196
197         node_pos = (char*)fdtp + node_offset;
198
199         phandle_pos = fdt_getprop_w(fdtp, node_offset, "phandle", &proplen);
200         if (phandle_pos)
201                 fdt_increase_u32(phandle_pos, phandle_offset);
202         phandle_pos = fdt_getprop_w(fdtp, node_offset, "linux,phandle", &proplen);
203         if (phandle_pos)
204                 fdt_increase_u32(phandle_pos, phandle_offset);
205 }
206
207 /*
208  * Increase all phandles by offset
209  */
210 static void
211 fdt_increase_phandles(void *fdtp, uint32_t offset)
212 {
213         int o, depth;
214
215         o = fdt_path_offset(fdtp, "/");
216         for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(fdtp, o, &depth)) {
217                 fdt_increase_phandle(fdtp, o, offset);
218         }
219 }
220
221 /*
222  * Overlay one node defined by <overlay_fdtp, overlay_o> over <main_fdtp, target_o>
223  */
224 static void
225 fdt_overlay_node(void *main_fdtp, int target_o, void *overlay_fdtp, int overlay_o)
226 {
227         int len, o, depth;
228         const char *name;
229         const void *val;
230         int target_subnode_o;
231
232         /* Overlay properties */
233         for (o = fdt_first_property_offset(overlay_fdtp, overlay_o);
234             o >= 0; o = fdt_next_property_offset(overlay_fdtp, o)) {
235                 val = fdt_getprop_by_offset(overlay_fdtp, o, &name, &len);
236                 if (val)
237                         fdt_setprop(main_fdtp, target_o, name, val, len);
238         }
239
240         /* Now overlay nodes */
241         o = overlay_o;
242         for (depth = 0; (o >= 0) && (depth >= 0);
243             o = fdt_next_node(overlay_fdtp, o, &depth)) {
244                 if (depth != 1)
245                         continue;
246                 /* Check if there is node with the same name */
247                 name = fdt_get_name(overlay_fdtp, o, NULL);
248                 target_subnode_o = fdt_subnode_offset(main_fdtp, target_o, name);
249                 if (target_subnode_o < 0) {
250                         /* create new subnode and run merge recursively */
251                         target_subnode_o = fdt_add_subnode(main_fdtp, target_o, name);
252                         if (target_subnode_o < 0) {
253                                 printf("failed to create subnode \"%s\": %d\n",
254                                     name, target_subnode_o);
255                                 return;
256                         }
257                 }
258
259                 fdt_overlay_node(main_fdtp, target_subnode_o,
260                     overlay_fdtp, o);
261         }
262 }
263
264 /*
265  * Apply one overlay fragment
266  */
267 static void
268 fdt_apply_fragment(void *main_fdtp, void *overlay_fdtp, int fragment_o)
269 {
270         uint32_t target;
271         const char *target_path;
272         const void *val;
273         int target_node_o, overlay_node_o;
274
275         target_node_o = -1;
276         val = fdt_getprop(overlay_fdtp, fragment_o, "target", NULL);
277         if (val) {
278                 memcpy(&target, val, sizeof(target));
279                 target = fdt32_to_cpu(target);
280                 target_node_o = fdt_node_offset_by_phandle(main_fdtp, target);
281                 if (target_node_o < 0) {
282                         printf("failed to find target %04x\n", target);
283                         return;
284                 }
285         }
286
287         if (target_node_o < 0) {
288                 target_path = fdt_getprop(overlay_fdtp, fragment_o, "target-path", NULL);
289                 if (target_path == NULL)
290                         return;
291
292                 target_node_o = fdt_path_offset(main_fdtp, target_path);
293                 if (target_node_o < 0) {
294                         printf("failed to find target-path %s\n", target_path);
295                         return;
296                 }
297         }
298
299         if (target_node_o < 0)
300                 return;
301
302         overlay_node_o = fdt_subnode_offset(overlay_fdtp, fragment_o, "__overlay__");
303         if (overlay_node_o < 0) {
304                 printf("missing __overlay__ sub-node\n");
305                 return;
306         }
307
308         fdt_overlay_node(main_fdtp, target_node_o, overlay_fdtp, overlay_node_o);
309 }
310
311 /*
312  * Handle __fixups__ node in overlay DTB
313  */
314 static int
315 fdt_overlay_do_fixups(void *main_fdtp, void *overlay_fdtp)
316 {
317         int main_symbols_o, symbol_o, overlay_fixups_o;
318         int fixup_prop_o;
319         int len;
320         const char *fixups, *name;
321         const char *symbol_path;
322         uint32_t phandle;
323
324         main_symbols_o = fdt_path_offset(main_fdtp, "/__symbols__");
325         overlay_fixups_o = fdt_path_offset(overlay_fdtp, "/__fixups__");
326
327         if (main_symbols_o < 0) {
328                 if (main_symbols_o == -FDT_ERR_NOTFOUND)
329                         return (0);
330                 return (-1);
331         }
332         if (overlay_fixups_o < 0) {
333                 if (overlay_fixups_o == -FDT_ERR_NOTFOUND)
334                         return (0);
335                 return (-1);
336         }
337
338         for (fixup_prop_o = fdt_first_property_offset(overlay_fdtp, overlay_fixups_o);
339             fixup_prop_o >= 0;
340             fixup_prop_o = fdt_next_property_offset(overlay_fdtp, fixup_prop_o)) {
341                 fixups = fdt_getprop_by_offset(overlay_fdtp, fixup_prop_o, &name, &len);
342                 symbol_path = fdt_getprop(main_fdtp, main_symbols_o, name, NULL);
343                 if (symbol_path == NULL) {
344                         printf("couldn't find \"%s\" symbol in main dtb\n", name);
345                         return (-1);
346                 }
347                 symbol_o = fdt_path_offset(main_fdtp, symbol_path);
348                 if (symbol_o < 0) {
349                         printf("couldn't find \"%s\" path in main dtb\n", symbol_path);
350                         return (-1);
351                 }
352                 phandle = fdt_get_phandle(main_fdtp, symbol_o);
353                 if (fdt_do_one_fixup(overlay_fdtp, fixups, len, phandle) < 0)
354                         return (-1);
355         }
356
357         return (0);
358 }
359
360 /*
361  * Handle __local_fixups__ node in overlay DTB
362  */
363 static int
364 fdt_overlay_do_local_fixups(void *main_fdtp, void *overlay_fdtp)
365 {
366         int overlay_local_fixups_o;
367         int len, rv;
368         const char *fixups;
369         uint32_t phandle_offset;
370
371         overlay_local_fixups_o = fdt_path_offset(overlay_fdtp, "/__local_fixups__");
372
373         if (overlay_local_fixups_o < 0) {
374                 /* If not local fixups - do nothing */
375                 if (overlay_local_fixups_o == -FDT_ERR_NOTFOUND)
376                         return (0);
377                 return (overlay_local_fixups_o);
378         }
379
380         phandle_offset = fdt_max_phandle(main_fdtp);
381         fdt_increase_phandles(overlay_fdtp, phandle_offset);
382         fixups = fdt_getprop_w(overlay_fdtp, overlay_local_fixups_o, "fixup", &len);
383         if (fixups) {
384                 rv = fdt_do_local_fixup(overlay_fdtp, fixups, len, phandle_offset);
385                 if (rv < 0)
386                         return (rv);
387         }
388
389         return (0);
390 }
391
392 /*
393  * Apply all fragments to main DTB
394  */
395 static int
396 fdt_overlay_apply_fragments(void *main_fdtp, void *overlay_fdtp)
397 {
398         int o, depth;
399
400         o = fdt_path_offset(overlay_fdtp, "/");
401         for (depth = 0; (o >= 0) && (depth >= 0); o = fdt_next_node(overlay_fdtp, o, &depth)) {
402                 if (depth != 1)
403                         continue;
404
405                 fdt_apply_fragment(main_fdtp, overlay_fdtp, o);
406         }
407
408         return (0);
409 }
410
411 int
412 fdt_overlay_apply(void *main_fdtp, void *overlay_fdtp)
413 {
414
415         if (fdt_overlay_do_fixups(main_fdtp, overlay_fdtp) < 0) {
416                 printf("failed to perform fixups in overlay\n");
417                 return (-1);
418         }
419
420         if (fdt_overlay_do_local_fixups(main_fdtp, overlay_fdtp) < 0) {
421                 printf("failed to perform local fixups in overlay\n");
422                 return (-1);
423         }
424
425         if (fdt_overlay_apply_fragments(main_fdtp, overlay_fdtp) < 0) {
426                 printf("failed to apply fragments\n");
427                 return (-1);
428         }
429
430         return (0);
431 }