1 // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
3 * libfdt - Flat Device Tree manipulation
4 * Copyright (C) 2006 David Gibson, IBM Corporation.
6 #include "libfdt_env.h"
11 #include "libfdt_internal.h"
13 static int fdt_blocks_misordered_(const void *fdt
,
14 int mem_rsv_size
, int struct_size
)
16 return (fdt_off_mem_rsvmap(fdt
) < FDT_ALIGN(sizeof(struct fdt_header
), 8))
17 || (fdt_off_dt_struct(fdt
) <
18 (fdt_off_mem_rsvmap(fdt
) + mem_rsv_size
))
19 || (fdt_off_dt_strings(fdt
) <
20 (fdt_off_dt_struct(fdt
) + struct_size
))
21 || (fdt_totalsize(fdt
) <
22 (fdt_off_dt_strings(fdt
) + fdt_size_dt_strings(fdt
)));
25 static int fdt_rw_probe_(void *fdt
)
29 if (fdt_version(fdt
) < 17)
30 return -FDT_ERR_BADVERSION
;
31 if (fdt_blocks_misordered_(fdt
, sizeof(struct fdt_reserve_entry
),
32 fdt_size_dt_struct(fdt
)))
33 return -FDT_ERR_BADLAYOUT
;
34 if (fdt_version(fdt
) > 17)
35 fdt_set_version(fdt
, 17);
40 #define FDT_RW_PROBE(fdt) \
43 if ((err_ = fdt_rw_probe_(fdt)) != 0) \
47 static inline int fdt_data_size_(void *fdt
)
49 return fdt_off_dt_strings(fdt
) + fdt_size_dt_strings(fdt
);
52 static int fdt_splice_(void *fdt
, void *splicepoint
, int oldlen
, int newlen
)
54 char *p
= splicepoint
;
55 char *end
= (char *)fdt
+ fdt_data_size_(fdt
);
57 if (((p
+ oldlen
) < p
) || ((p
+ oldlen
) > end
))
58 return -FDT_ERR_BADOFFSET
;
59 if ((p
< (char *)fdt
) || ((end
- oldlen
+ newlen
) < (char *)fdt
))
60 return -FDT_ERR_BADOFFSET
;
61 if ((end
- oldlen
+ newlen
) > ((char *)fdt
+ fdt_totalsize(fdt
)))
62 return -FDT_ERR_NOSPACE
;
63 memmove(p
+ newlen
, p
+ oldlen
, end
- p
- oldlen
);
67 static int fdt_splice_mem_rsv_(void *fdt
, struct fdt_reserve_entry
*p
,
70 int delta
= (newn
- oldn
) * sizeof(*p
);
72 err
= fdt_splice_(fdt
, p
, oldn
* sizeof(*p
), newn
* sizeof(*p
));
75 fdt_set_off_dt_struct(fdt
, fdt_off_dt_struct(fdt
) + delta
);
76 fdt_set_off_dt_strings(fdt
, fdt_off_dt_strings(fdt
) + delta
);
80 static int fdt_splice_struct_(void *fdt
, void *p
,
81 int oldlen
, int newlen
)
83 int delta
= newlen
- oldlen
;
86 if ((err
= fdt_splice_(fdt
, p
, oldlen
, newlen
)))
89 fdt_set_size_dt_struct(fdt
, fdt_size_dt_struct(fdt
) + delta
);
90 fdt_set_off_dt_strings(fdt
, fdt_off_dt_strings(fdt
) + delta
);
94 /* Must only be used to roll back in case of error */
95 static void fdt_del_last_string_(void *fdt
, const char *s
)
97 int newlen
= strlen(s
) + 1;
99 fdt_set_size_dt_strings(fdt
, fdt_size_dt_strings(fdt
) - newlen
);
102 static int fdt_splice_string_(void *fdt
, int newlen
)
104 void *p
= (char *)fdt
105 + fdt_off_dt_strings(fdt
) + fdt_size_dt_strings(fdt
);
108 if ((err
= fdt_splice_(fdt
, p
, 0, newlen
)))
111 fdt_set_size_dt_strings(fdt
, fdt_size_dt_strings(fdt
) + newlen
);
115 static int fdt_find_add_string_(void *fdt
, const char *s
, int *allocated
)
117 char *strtab
= (char *)fdt
+ fdt_off_dt_strings(fdt
);
120 int len
= strlen(s
) + 1;
125 p
= fdt_find_string_(strtab
, fdt_size_dt_strings(fdt
), s
);
130 new = strtab
+ fdt_size_dt_strings(fdt
);
131 err
= fdt_splice_string_(fdt
, len
);
138 return (new - strtab
);
141 int fdt_add_mem_rsv(void *fdt
, uint64_t address
, uint64_t size
)
143 struct fdt_reserve_entry
*re
;
148 re
= fdt_mem_rsv_w_(fdt
, fdt_num_mem_rsv(fdt
));
149 err
= fdt_splice_mem_rsv_(fdt
, re
, 0, 1);
153 re
->address
= cpu_to_fdt64(address
);
154 re
->size
= cpu_to_fdt64(size
);
158 int fdt_del_mem_rsv(void *fdt
, int n
)
160 struct fdt_reserve_entry
*re
= fdt_mem_rsv_w_(fdt
, n
);
164 if (n
>= fdt_num_mem_rsv(fdt
))
165 return -FDT_ERR_NOTFOUND
;
167 return fdt_splice_mem_rsv_(fdt
, re
, 1, 0);
170 static int fdt_resize_property_(void *fdt
, int nodeoffset
, const char *name
,
171 int len
, struct fdt_property
**prop
)
176 *prop
= fdt_get_property_w(fdt
, nodeoffset
, name
, &oldlen
);
180 if ((err
= fdt_splice_struct_(fdt
, (*prop
)->data
, FDT_TAGALIGN(oldlen
),
184 (*prop
)->len
= cpu_to_fdt32(len
);
188 static int fdt_add_property_(void *fdt
, int nodeoffset
, const char *name
,
189 int len
, struct fdt_property
**prop
)
197 if ((nextoffset
= fdt_check_node_offset_(fdt
, nodeoffset
)) < 0)
200 namestroff
= fdt_find_add_string_(fdt
, name
, &allocated
);
204 *prop
= fdt_offset_ptr_w_(fdt
, nextoffset
);
205 proplen
= sizeof(**prop
) + FDT_TAGALIGN(len
);
207 err
= fdt_splice_struct_(fdt
, *prop
, 0, proplen
);
210 fdt_del_last_string_(fdt
, name
);
214 (*prop
)->tag
= cpu_to_fdt32(FDT_PROP
);
215 (*prop
)->nameoff
= cpu_to_fdt32(namestroff
);
216 (*prop
)->len
= cpu_to_fdt32(len
);
220 int fdt_set_name(void *fdt
, int nodeoffset
, const char *name
)
228 namep
= (char *)(uintptr_t)fdt_get_name(fdt
, nodeoffset
, &oldlen
);
232 newlen
= strlen(name
);
234 err
= fdt_splice_struct_(fdt
, namep
, FDT_TAGALIGN(oldlen
+1),
235 FDT_TAGALIGN(newlen
+1));
239 memcpy(namep
, name
, newlen
+1);
243 int fdt_setprop_placeholder(void *fdt
, int nodeoffset
, const char *name
,
244 int len
, void **prop_data
)
246 struct fdt_property
*prop
;
251 err
= fdt_resize_property_(fdt
, nodeoffset
, name
, len
, &prop
);
252 if (err
== -FDT_ERR_NOTFOUND
)
253 err
= fdt_add_property_(fdt
, nodeoffset
, name
, len
, &prop
);
257 *prop_data
= prop
->data
;
261 int fdt_setprop(void *fdt
, int nodeoffset
, const char *name
,
262 const void *val
, int len
)
267 err
= fdt_setprop_placeholder(fdt
, nodeoffset
, name
, len
, &prop_data
);
272 memcpy(prop_data
, val
, len
);
276 int fdt_appendprop(void *fdt
, int nodeoffset
, const char *name
,
277 const void *val
, int len
)
279 struct fdt_property
*prop
;
280 int err
, oldlen
, newlen
;
284 prop
= fdt_get_property_w(fdt
, nodeoffset
, name
, &oldlen
);
286 newlen
= len
+ oldlen
;
287 err
= fdt_splice_struct_(fdt
, prop
->data
,
288 FDT_TAGALIGN(oldlen
),
289 FDT_TAGALIGN(newlen
));
292 prop
->len
= cpu_to_fdt32(newlen
);
293 memcpy(prop
->data
+ oldlen
, val
, len
);
295 err
= fdt_add_property_(fdt
, nodeoffset
, name
, len
, &prop
);
298 memcpy(prop
->data
, val
, len
);
303 int fdt_delprop(void *fdt
, int nodeoffset
, const char *name
)
305 struct fdt_property
*prop
;
310 prop
= fdt_get_property_w(fdt
, nodeoffset
, name
, &len
);
314 proplen
= sizeof(*prop
) + FDT_TAGALIGN(len
);
315 return fdt_splice_struct_(fdt
, prop
, proplen
, 0);
318 int fdt_add_subnode_namelen(void *fdt
, int parentoffset
,
319 const char *name
, int namelen
)
321 struct fdt_node_header
*nh
;
322 int offset
, nextoffset
;
330 offset
= fdt_subnode_offset_namelen(fdt
, parentoffset
, name
, namelen
);
332 return -FDT_ERR_EXISTS
;
333 else if (offset
!= -FDT_ERR_NOTFOUND
)
336 /* Try to place the new node after the parent's properties */
337 fdt_next_tag(fdt
, parentoffset
, &nextoffset
); /* skip the BEGIN_NODE */
340 tag
= fdt_next_tag(fdt
, offset
, &nextoffset
);
341 } while ((tag
== FDT_PROP
) || (tag
== FDT_NOP
));
343 nh
= fdt_offset_ptr_w_(fdt
, offset
);
344 nodelen
= sizeof(*nh
) + FDT_TAGALIGN(namelen
+1) + FDT_TAGSIZE
;
346 err
= fdt_splice_struct_(fdt
, nh
, 0, nodelen
);
350 nh
->tag
= cpu_to_fdt32(FDT_BEGIN_NODE
);
351 memset(nh
->name
, 0, FDT_TAGALIGN(namelen
+1));
352 memcpy(nh
->name
, name
, namelen
);
353 endtag
= (fdt32_t
*)((char *)nh
+ nodelen
- FDT_TAGSIZE
);
354 *endtag
= cpu_to_fdt32(FDT_END_NODE
);
359 int fdt_add_subnode(void *fdt
, int parentoffset
, const char *name
)
361 return fdt_add_subnode_namelen(fdt
, parentoffset
, name
, strlen(name
));
364 int fdt_del_node(void *fdt
, int nodeoffset
)
370 endoffset
= fdt_node_end_offset_(fdt
, nodeoffset
);
374 return fdt_splice_struct_(fdt
, fdt_offset_ptr_w_(fdt
, nodeoffset
),
375 endoffset
- nodeoffset
, 0);
378 static void fdt_packblocks_(const char *old
, char *new,
379 int mem_rsv_size
, int struct_size
)
381 int mem_rsv_off
, struct_off
, strings_off
;
383 mem_rsv_off
= FDT_ALIGN(sizeof(struct fdt_header
), 8);
384 struct_off
= mem_rsv_off
+ mem_rsv_size
;
385 strings_off
= struct_off
+ struct_size
;
387 memmove(new + mem_rsv_off
, old
+ fdt_off_mem_rsvmap(old
), mem_rsv_size
);
388 fdt_set_off_mem_rsvmap(new, mem_rsv_off
);
390 memmove(new + struct_off
, old
+ fdt_off_dt_struct(old
), struct_size
);
391 fdt_set_off_dt_struct(new, struct_off
);
392 fdt_set_size_dt_struct(new, struct_size
);
394 memmove(new + strings_off
, old
+ fdt_off_dt_strings(old
),
395 fdt_size_dt_strings(old
));
396 fdt_set_off_dt_strings(new, strings_off
);
397 fdt_set_size_dt_strings(new, fdt_size_dt_strings(old
));
400 int fdt_open_into(const void *fdt
, void *buf
, int bufsize
)
403 int mem_rsv_size
, struct_size
;
405 const char *fdtstart
= fdt
;
406 const char *fdtend
= fdtstart
+ fdt_totalsize(fdt
);
411 mem_rsv_size
= (fdt_num_mem_rsv(fdt
)+1)
412 * sizeof(struct fdt_reserve_entry
);
414 if (fdt_version(fdt
) >= 17) {
415 struct_size
= fdt_size_dt_struct(fdt
);
418 while (fdt_next_tag(fdt
, struct_size
, &struct_size
) != FDT_END
)
424 if (!fdt_blocks_misordered_(fdt
, mem_rsv_size
, struct_size
)) {
425 /* no further work necessary */
426 err
= fdt_move(fdt
, buf
, bufsize
);
429 fdt_set_version(buf
, 17);
430 fdt_set_size_dt_struct(buf
, struct_size
);
431 fdt_set_totalsize(buf
, bufsize
);
435 /* Need to reorder */
436 newsize
= FDT_ALIGN(sizeof(struct fdt_header
), 8) + mem_rsv_size
437 + struct_size
+ fdt_size_dt_strings(fdt
);
439 if (bufsize
< newsize
)
440 return -FDT_ERR_NOSPACE
;
442 /* First attempt to build converted tree at beginning of buffer */
444 /* But if that overlaps with the old tree... */
445 if (((tmp
+ newsize
) > fdtstart
) && (tmp
< fdtend
)) {
446 /* Try right after the old tree instead */
447 tmp
= (char *)(uintptr_t)fdtend
;
448 if ((tmp
+ newsize
) > ((char *)buf
+ bufsize
))
449 return -FDT_ERR_NOSPACE
;
452 fdt_packblocks_(fdt
, tmp
, mem_rsv_size
, struct_size
);
453 memmove(buf
, tmp
, newsize
);
455 fdt_set_magic(buf
, FDT_MAGIC
);
456 fdt_set_totalsize(buf
, bufsize
);
457 fdt_set_version(buf
, 17);
458 fdt_set_last_comp_version(buf
, 16);
459 fdt_set_boot_cpuid_phys(buf
, fdt_boot_cpuid_phys(fdt
));
464 int fdt_pack(void *fdt
)
470 mem_rsv_size
= (fdt_num_mem_rsv(fdt
)+1)
471 * sizeof(struct fdt_reserve_entry
);
472 fdt_packblocks_(fdt
, fdt
, mem_rsv_size
, fdt_size_dt_struct(fdt
));
473 fdt_set_totalsize(fdt
, fdt_data_size_(fdt
));