xref: /optee_os/core/lib/libfdt/fdt_rw.c (revision 17f326eba66aab9ea16549754a95252dd5551a13)
1*17f326ebSJerome Forissier // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2b908c675SJens Wiklander /*
3b908c675SJens Wiklander  * libfdt - Flat Device Tree manipulation
4b908c675SJens Wiklander  * Copyright (C) 2006 David Gibson, IBM Corporation.
5b908c675SJens Wiklander  */
6b908c675SJens Wiklander #include "libfdt_env.h"
7b908c675SJens Wiklander 
8b908c675SJens Wiklander #include <fdt.h>
9b908c675SJens Wiklander #include <libfdt.h>
10b908c675SJens Wiklander 
11b908c675SJens Wiklander #include "libfdt_internal.h"
12b908c675SJens Wiklander 
fdt_blocks_misordered_(const void * fdt,int mem_rsv_size,int struct_size)1301d6a9daSBryan O'Donoghue static int fdt_blocks_misordered_(const void *fdt,
14b908c675SJens Wiklander 				  int mem_rsv_size, int struct_size)
15b908c675SJens Wiklander {
16b908c675SJens Wiklander 	return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
17b908c675SJens Wiklander 		|| (fdt_off_dt_struct(fdt) <
18b908c675SJens Wiklander 		    (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
19b908c675SJens Wiklander 		|| (fdt_off_dt_strings(fdt) <
20b908c675SJens Wiklander 		    (fdt_off_dt_struct(fdt) + struct_size))
21b908c675SJens Wiklander 		|| (fdt_totalsize(fdt) <
22b908c675SJens Wiklander 		    (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
23b908c675SJens Wiklander }
24b908c675SJens Wiklander 
fdt_rw_probe_(void * fdt)25*17f326ebSJerome Forissier static int fdt_rw_probe_(void *fdt)
26b908c675SJens Wiklander {
27*17f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
28b908c675SJens Wiklander 
29b908c675SJens Wiklander 	if (fdt_version(fdt) < 17)
30b908c675SJens Wiklander 		return -FDT_ERR_BADVERSION;
3101d6a9daSBryan O'Donoghue 	if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
32b908c675SJens Wiklander 				   fdt_size_dt_struct(fdt)))
33b908c675SJens Wiklander 		return -FDT_ERR_BADLAYOUT;
34b908c675SJens Wiklander 	if (fdt_version(fdt) > 17)
35b908c675SJens Wiklander 		fdt_set_version(fdt, 17);
36b908c675SJens Wiklander 
37b908c675SJens Wiklander 	return 0;
38b908c675SJens Wiklander }
39b908c675SJens Wiklander 
40*17f326ebSJerome Forissier #define FDT_RW_PROBE(fdt) \
41b908c675SJens Wiklander 	{ \
4201d6a9daSBryan O'Donoghue 		int err_; \
43*17f326ebSJerome Forissier 		if ((err_ = fdt_rw_probe_(fdt)) != 0) \
4401d6a9daSBryan O'Donoghue 			return err_; \
45b908c675SJens Wiklander 	}
46b908c675SJens Wiklander 
fdt_data_size_(void * fdt)4701d6a9daSBryan O'Donoghue static inline int fdt_data_size_(void *fdt)
48b908c675SJens Wiklander {
49b908c675SJens Wiklander 	return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
50b908c675SJens Wiklander }
51b908c675SJens Wiklander 
fdt_splice_(void * fdt,void * splicepoint,int oldlen,int newlen)5201d6a9daSBryan O'Donoghue static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
53b908c675SJens Wiklander {
54b908c675SJens Wiklander 	char *p = splicepoint;
5501d6a9daSBryan O'Donoghue 	char *end = (char *)fdt + fdt_data_size_(fdt);
56b908c675SJens Wiklander 
57b908c675SJens Wiklander 	if (((p + oldlen) < p) || ((p + oldlen) > end))
58b908c675SJens Wiklander 		return -FDT_ERR_BADOFFSET;
5901d6a9daSBryan O'Donoghue 	if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
6001d6a9daSBryan O'Donoghue 		return -FDT_ERR_BADOFFSET;
61b908c675SJens Wiklander 	if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
62b908c675SJens Wiklander 		return -FDT_ERR_NOSPACE;
63b908c675SJens Wiklander 	memmove(p + newlen, p + oldlen, end - p - oldlen);
64b908c675SJens Wiklander 	return 0;
65b908c675SJens Wiklander }
66b908c675SJens Wiklander 
fdt_splice_mem_rsv_(void * fdt,struct fdt_reserve_entry * p,int oldn,int newn)6701d6a9daSBryan O'Donoghue static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
68b908c675SJens Wiklander 			       int oldn, int newn)
69b908c675SJens Wiklander {
70b908c675SJens Wiklander 	int delta = (newn - oldn) * sizeof(*p);
71b908c675SJens Wiklander 	int err;
7201d6a9daSBryan O'Donoghue 	err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
73b908c675SJens Wiklander 	if (err)
74b908c675SJens Wiklander 		return err;
75b908c675SJens Wiklander 	fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
76b908c675SJens Wiklander 	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
77b908c675SJens Wiklander 	return 0;
78b908c675SJens Wiklander }
79b908c675SJens Wiklander 
fdt_splice_struct_(void * fdt,void * p,int oldlen,int newlen)8001d6a9daSBryan O'Donoghue static int fdt_splice_struct_(void *fdt, void *p,
81b908c675SJens Wiklander 			      int oldlen, int newlen)
82b908c675SJens Wiklander {
83b908c675SJens Wiklander 	int delta = newlen - oldlen;
84b908c675SJens Wiklander 	int err;
85b908c675SJens Wiklander 
8601d6a9daSBryan O'Donoghue 	if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
87b908c675SJens Wiklander 		return err;
88b908c675SJens Wiklander 
89b908c675SJens Wiklander 	fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
90b908c675SJens Wiklander 	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
91b908c675SJens Wiklander 	return 0;
92b908c675SJens Wiklander }
93b908c675SJens Wiklander 
94*17f326ebSJerome Forissier /* Must only be used to roll back in case of error */
fdt_del_last_string_(void * fdt,const char * s)95*17f326ebSJerome Forissier static void fdt_del_last_string_(void *fdt, const char *s)
96*17f326ebSJerome Forissier {
97*17f326ebSJerome Forissier 	int newlen = strlen(s) + 1;
98*17f326ebSJerome Forissier 
99*17f326ebSJerome Forissier 	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
100*17f326ebSJerome Forissier }
101*17f326ebSJerome Forissier 
fdt_splice_string_(void * fdt,int newlen)10201d6a9daSBryan O'Donoghue static int fdt_splice_string_(void *fdt, int newlen)
103b908c675SJens Wiklander {
104b908c675SJens Wiklander 	void *p = (char *)fdt
105b908c675SJens Wiklander 		+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
106b908c675SJens Wiklander 	int err;
107b908c675SJens Wiklander 
10801d6a9daSBryan O'Donoghue 	if ((err = fdt_splice_(fdt, p, 0, newlen)))
109b908c675SJens Wiklander 		return err;
110b908c675SJens Wiklander 
111b908c675SJens Wiklander 	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
112b908c675SJens Wiklander 	return 0;
113b908c675SJens Wiklander }
114b908c675SJens Wiklander 
fdt_find_add_string_(void * fdt,const char * s,int * allocated)115*17f326ebSJerome Forissier static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
116b908c675SJens Wiklander {
117b908c675SJens Wiklander 	char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
118b908c675SJens Wiklander 	const char *p;
119b908c675SJens Wiklander 	char *new;
120b908c675SJens Wiklander 	int len = strlen(s) + 1;
121b908c675SJens Wiklander 	int err;
122b908c675SJens Wiklander 
123*17f326ebSJerome Forissier 	*allocated = 0;
124*17f326ebSJerome Forissier 
12501d6a9daSBryan O'Donoghue 	p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s);
126b908c675SJens Wiklander 	if (p)
127b908c675SJens Wiklander 		/* found it */
128b908c675SJens Wiklander 		return (p - strtab);
129b908c675SJens Wiklander 
130b908c675SJens Wiklander 	new = strtab + fdt_size_dt_strings(fdt);
13101d6a9daSBryan O'Donoghue 	err = fdt_splice_string_(fdt, len);
132b908c675SJens Wiklander 	if (err)
133b908c675SJens Wiklander 		return err;
134b908c675SJens Wiklander 
135*17f326ebSJerome Forissier 	*allocated = 1;
136*17f326ebSJerome Forissier 
137b908c675SJens Wiklander 	memcpy(new, s, len);
138b908c675SJens Wiklander 	return (new - strtab);
139b908c675SJens Wiklander }
140b908c675SJens Wiklander 
fdt_add_mem_rsv(void * fdt,uint64_t address,uint64_t size)141b908c675SJens Wiklander int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
142b908c675SJens Wiklander {
143b908c675SJens Wiklander 	struct fdt_reserve_entry *re;
144b908c675SJens Wiklander 	int err;
145b908c675SJens Wiklander 
146*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
147b908c675SJens Wiklander 
14801d6a9daSBryan O'Donoghue 	re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
14901d6a9daSBryan O'Donoghue 	err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
150b908c675SJens Wiklander 	if (err)
151b908c675SJens Wiklander 		return err;
152b908c675SJens Wiklander 
153b908c675SJens Wiklander 	re->address = cpu_to_fdt64(address);
154b908c675SJens Wiklander 	re->size = cpu_to_fdt64(size);
155b908c675SJens Wiklander 	return 0;
156b908c675SJens Wiklander }
157b908c675SJens Wiklander 
fdt_del_mem_rsv(void * fdt,int n)158b908c675SJens Wiklander int fdt_del_mem_rsv(void *fdt, int n)
159b908c675SJens Wiklander {
16001d6a9daSBryan O'Donoghue 	struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
161b908c675SJens Wiklander 
162*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
163b908c675SJens Wiklander 
164b908c675SJens Wiklander 	if (n >= fdt_num_mem_rsv(fdt))
165b908c675SJens Wiklander 		return -FDT_ERR_NOTFOUND;
166b908c675SJens Wiklander 
16701d6a9daSBryan O'Donoghue 	return fdt_splice_mem_rsv_(fdt, re, 1, 0);
168b908c675SJens Wiklander }
169b908c675SJens Wiklander 
fdt_resize_property_(void * fdt,int nodeoffset,const char * name,int len,struct fdt_property ** prop)17001d6a9daSBryan O'Donoghue static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
171b908c675SJens Wiklander 				int len, struct fdt_property **prop)
172b908c675SJens Wiklander {
173b908c675SJens Wiklander 	int oldlen;
174b908c675SJens Wiklander 	int err;
175b908c675SJens Wiklander 
176b908c675SJens Wiklander 	*prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
17701d6a9daSBryan O'Donoghue 	if (!*prop)
178b908c675SJens Wiklander 		return oldlen;
179b908c675SJens Wiklander 
18001d6a9daSBryan O'Donoghue 	if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
181b908c675SJens Wiklander 				      FDT_TAGALIGN(len))))
182b908c675SJens Wiklander 		return err;
183b908c675SJens Wiklander 
184b908c675SJens Wiklander 	(*prop)->len = cpu_to_fdt32(len);
185b908c675SJens Wiklander 	return 0;
186b908c675SJens Wiklander }
187b908c675SJens Wiklander 
fdt_add_property_(void * fdt,int nodeoffset,const char * name,int len,struct fdt_property ** prop)18801d6a9daSBryan O'Donoghue static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
189b908c675SJens Wiklander 			     int len, struct fdt_property **prop)
190b908c675SJens Wiklander {
191b908c675SJens Wiklander 	int proplen;
192b908c675SJens Wiklander 	int nextoffset;
193b908c675SJens Wiklander 	int namestroff;
194b908c675SJens Wiklander 	int err;
195*17f326ebSJerome Forissier 	int allocated;
196b908c675SJens Wiklander 
19701d6a9daSBryan O'Donoghue 	if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
198b908c675SJens Wiklander 		return nextoffset;
199b908c675SJens Wiklander 
200*17f326ebSJerome Forissier 	namestroff = fdt_find_add_string_(fdt, name, &allocated);
201b908c675SJens Wiklander 	if (namestroff < 0)
202b908c675SJens Wiklander 		return namestroff;
203b908c675SJens Wiklander 
20401d6a9daSBryan O'Donoghue 	*prop = fdt_offset_ptr_w_(fdt, nextoffset);
205b908c675SJens Wiklander 	proplen = sizeof(**prop) + FDT_TAGALIGN(len);
206b908c675SJens Wiklander 
20701d6a9daSBryan O'Donoghue 	err = fdt_splice_struct_(fdt, *prop, 0, proplen);
208*17f326ebSJerome Forissier 	if (err) {
209*17f326ebSJerome Forissier 		if (allocated)
210*17f326ebSJerome Forissier 			fdt_del_last_string_(fdt, name);
211b908c675SJens Wiklander 		return err;
212*17f326ebSJerome Forissier 	}
213b908c675SJens Wiklander 
214b908c675SJens Wiklander 	(*prop)->tag = cpu_to_fdt32(FDT_PROP);
215b908c675SJens Wiklander 	(*prop)->nameoff = cpu_to_fdt32(namestroff);
216b908c675SJens Wiklander 	(*prop)->len = cpu_to_fdt32(len);
217b908c675SJens Wiklander 	return 0;
218b908c675SJens Wiklander }
219b908c675SJens Wiklander 
fdt_set_name(void * fdt,int nodeoffset,const char * name)220b908c675SJens Wiklander int fdt_set_name(void *fdt, int nodeoffset, const char *name)
221b908c675SJens Wiklander {
222b908c675SJens Wiklander 	char *namep;
223b908c675SJens Wiklander 	int oldlen, newlen;
224b908c675SJens Wiklander 	int err;
225b908c675SJens Wiklander 
226*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
227b908c675SJens Wiklander 
228b908c675SJens Wiklander 	namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
229b908c675SJens Wiklander 	if (!namep)
230b908c675SJens Wiklander 		return oldlen;
231b908c675SJens Wiklander 
232b908c675SJens Wiklander 	newlen = strlen(name);
233b908c675SJens Wiklander 
23401d6a9daSBryan O'Donoghue 	err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
235b908c675SJens Wiklander 				 FDT_TAGALIGN(newlen+1));
236b908c675SJens Wiklander 	if (err)
237b908c675SJens Wiklander 		return err;
238b908c675SJens Wiklander 
239b908c675SJens Wiklander 	memcpy(namep, name, newlen+1);
240b908c675SJens Wiklander 	return 0;
241b908c675SJens Wiklander }
242b908c675SJens Wiklander 
fdt_setprop_placeholder(void * fdt,int nodeoffset,const char * name,int len,void ** prop_data)24301d6a9daSBryan O'Donoghue int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
24401d6a9daSBryan O'Donoghue 			    int len, void **prop_data)
245b908c675SJens Wiklander {
246b908c675SJens Wiklander 	struct fdt_property *prop;
247b908c675SJens Wiklander 	int err;
248b908c675SJens Wiklander 
249*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
250b908c675SJens Wiklander 
25101d6a9daSBryan O'Donoghue 	err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop);
252b908c675SJens Wiklander 	if (err == -FDT_ERR_NOTFOUND)
25301d6a9daSBryan O'Donoghue 		err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
25401d6a9daSBryan O'Donoghue 	if (err)
25501d6a9daSBryan O'Donoghue 		return err;
25601d6a9daSBryan O'Donoghue 
25701d6a9daSBryan O'Donoghue 	*prop_data = prop->data;
25801d6a9daSBryan O'Donoghue 	return 0;
25901d6a9daSBryan O'Donoghue }
26001d6a9daSBryan O'Donoghue 
fdt_setprop(void * fdt,int nodeoffset,const char * name,const void * val,int len)26101d6a9daSBryan O'Donoghue int fdt_setprop(void *fdt, int nodeoffset, const char *name,
26201d6a9daSBryan O'Donoghue 		const void *val, int len)
26301d6a9daSBryan O'Donoghue {
26401d6a9daSBryan O'Donoghue 	void *prop_data;
26501d6a9daSBryan O'Donoghue 	int err;
26601d6a9daSBryan O'Donoghue 
26701d6a9daSBryan O'Donoghue 	err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data);
268b908c675SJens Wiklander 	if (err)
269b908c675SJens Wiklander 		return err;
270b908c675SJens Wiklander 
2715f51bfdaSJens Wiklander 	if (len)
27201d6a9daSBryan O'Donoghue 		memcpy(prop_data, val, len);
273b908c675SJens Wiklander 	return 0;
274b908c675SJens Wiklander }
275b908c675SJens Wiklander 
fdt_appendprop(void * fdt,int nodeoffset,const char * name,const void * val,int len)276b908c675SJens Wiklander int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
277b908c675SJens Wiklander 		   const void *val, int len)
278b908c675SJens Wiklander {
279b908c675SJens Wiklander 	struct fdt_property *prop;
280b908c675SJens Wiklander 	int err, oldlen, newlen;
281b908c675SJens Wiklander 
282*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
283b908c675SJens Wiklander 
284b908c675SJens Wiklander 	prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
285b908c675SJens Wiklander 	if (prop) {
286b908c675SJens Wiklander 		newlen = len + oldlen;
28701d6a9daSBryan O'Donoghue 		err = fdt_splice_struct_(fdt, prop->data,
288b908c675SJens Wiklander 					 FDT_TAGALIGN(oldlen),
289b908c675SJens Wiklander 					 FDT_TAGALIGN(newlen));
290b908c675SJens Wiklander 		if (err)
291b908c675SJens Wiklander 			return err;
292b908c675SJens Wiklander 		prop->len = cpu_to_fdt32(newlen);
293b908c675SJens Wiklander 		memcpy(prop->data + oldlen, val, len);
294b908c675SJens Wiklander 	} else {
29501d6a9daSBryan O'Donoghue 		err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
296b908c675SJens Wiklander 		if (err)
297b908c675SJens Wiklander 			return err;
298b908c675SJens Wiklander 		memcpy(prop->data, val, len);
299b908c675SJens Wiklander 	}
300b908c675SJens Wiklander 	return 0;
301b908c675SJens Wiklander }
302b908c675SJens Wiklander 
fdt_delprop(void * fdt,int nodeoffset,const char * name)303b908c675SJens Wiklander int fdt_delprop(void *fdt, int nodeoffset, const char *name)
304b908c675SJens Wiklander {
305b908c675SJens Wiklander 	struct fdt_property *prop;
306b908c675SJens Wiklander 	int len, proplen;
307b908c675SJens Wiklander 
308*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
309b908c675SJens Wiklander 
310b908c675SJens Wiklander 	prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
311b908c675SJens Wiklander 	if (!prop)
312b908c675SJens Wiklander 		return len;
313b908c675SJens Wiklander 
314b908c675SJens Wiklander 	proplen = sizeof(*prop) + FDT_TAGALIGN(len);
31501d6a9daSBryan O'Donoghue 	return fdt_splice_struct_(fdt, prop, proplen, 0);
316b908c675SJens Wiklander }
317b908c675SJens Wiklander 
fdt_add_subnode_namelen(void * fdt,int parentoffset,const char * name,int namelen)318b908c675SJens Wiklander int fdt_add_subnode_namelen(void *fdt, int parentoffset,
319b908c675SJens Wiklander 			    const char *name, int namelen)
320b908c675SJens Wiklander {
321b908c675SJens Wiklander 	struct fdt_node_header *nh;
322b908c675SJens Wiklander 	int offset, nextoffset;
323b908c675SJens Wiklander 	int nodelen;
324b908c675SJens Wiklander 	int err;
325b908c675SJens Wiklander 	uint32_t tag;
326b908c675SJens Wiklander 	fdt32_t *endtag;
327b908c675SJens Wiklander 
328*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
329b908c675SJens Wiklander 
330b908c675SJens Wiklander 	offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
331b908c675SJens Wiklander 	if (offset >= 0)
332b908c675SJens Wiklander 		return -FDT_ERR_EXISTS;
333b908c675SJens Wiklander 	else if (offset != -FDT_ERR_NOTFOUND)
334b908c675SJens Wiklander 		return offset;
335b908c675SJens Wiklander 
336b908c675SJens Wiklander 	/* Try to place the new node after the parent's properties */
337b908c675SJens Wiklander 	fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
338b908c675SJens Wiklander 	do {
339b908c675SJens Wiklander 		offset = nextoffset;
340b908c675SJens Wiklander 		tag = fdt_next_tag(fdt, offset, &nextoffset);
341b908c675SJens Wiklander 	} while ((tag == FDT_PROP) || (tag == FDT_NOP));
342b908c675SJens Wiklander 
34301d6a9daSBryan O'Donoghue 	nh = fdt_offset_ptr_w_(fdt, offset);
344b908c675SJens Wiklander 	nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
345b908c675SJens Wiklander 
34601d6a9daSBryan O'Donoghue 	err = fdt_splice_struct_(fdt, nh, 0, nodelen);
347b908c675SJens Wiklander 	if (err)
348b908c675SJens Wiklander 		return err;
349b908c675SJens Wiklander 
350b908c675SJens Wiklander 	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
351b908c675SJens Wiklander 	memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
352b908c675SJens Wiklander 	memcpy(nh->name, name, namelen);
353b908c675SJens Wiklander 	endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
354b908c675SJens Wiklander 	*endtag = cpu_to_fdt32(FDT_END_NODE);
355b908c675SJens Wiklander 
356b908c675SJens Wiklander 	return offset;
357b908c675SJens Wiklander }
358b908c675SJens Wiklander 
fdt_add_subnode(void * fdt,int parentoffset,const char * name)359b908c675SJens Wiklander int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
360b908c675SJens Wiklander {
361b908c675SJens Wiklander 	return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
362b908c675SJens Wiklander }
363b908c675SJens Wiklander 
fdt_del_node(void * fdt,int nodeoffset)364b908c675SJens Wiklander int fdt_del_node(void *fdt, int nodeoffset)
365b908c675SJens Wiklander {
366b908c675SJens Wiklander 	int endoffset;
367b908c675SJens Wiklander 
368*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
369b908c675SJens Wiklander 
37001d6a9daSBryan O'Donoghue 	endoffset = fdt_node_end_offset_(fdt, nodeoffset);
371b908c675SJens Wiklander 	if (endoffset < 0)
372b908c675SJens Wiklander 		return endoffset;
373b908c675SJens Wiklander 
37401d6a9daSBryan O'Donoghue 	return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
375b908c675SJens Wiklander 				  endoffset - nodeoffset, 0);
376b908c675SJens Wiklander }
377b908c675SJens Wiklander 
fdt_packblocks_(const char * old,char * new,int mem_rsv_size,int struct_size)37801d6a9daSBryan O'Donoghue static void fdt_packblocks_(const char *old, char *new,
379b908c675SJens Wiklander 			    int mem_rsv_size, int struct_size)
380b908c675SJens Wiklander {
381b908c675SJens Wiklander 	int mem_rsv_off, struct_off, strings_off;
382b908c675SJens Wiklander 
383b908c675SJens Wiklander 	mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
384b908c675SJens Wiklander 	struct_off = mem_rsv_off + mem_rsv_size;
385b908c675SJens Wiklander 	strings_off = struct_off + struct_size;
386b908c675SJens Wiklander 
387b908c675SJens Wiklander 	memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
388b908c675SJens Wiklander 	fdt_set_off_mem_rsvmap(new, mem_rsv_off);
389b908c675SJens Wiklander 
390b908c675SJens Wiklander 	memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
391b908c675SJens Wiklander 	fdt_set_off_dt_struct(new, struct_off);
392b908c675SJens Wiklander 	fdt_set_size_dt_struct(new, struct_size);
393b908c675SJens Wiklander 
394b908c675SJens Wiklander 	memmove(new + strings_off, old + fdt_off_dt_strings(old),
395b908c675SJens Wiklander 		fdt_size_dt_strings(old));
396b908c675SJens Wiklander 	fdt_set_off_dt_strings(new, strings_off);
397b908c675SJens Wiklander 	fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
398b908c675SJens Wiklander }
399b908c675SJens Wiklander 
fdt_open_into(const void * fdt,void * buf,int bufsize)400b908c675SJens Wiklander int fdt_open_into(const void *fdt, void *buf, int bufsize)
401b908c675SJens Wiklander {
402b908c675SJens Wiklander 	int err;
403b908c675SJens Wiklander 	int mem_rsv_size, struct_size;
404b908c675SJens Wiklander 	int newsize;
405b908c675SJens Wiklander 	const char *fdtstart = fdt;
406b908c675SJens Wiklander 	const char *fdtend = fdtstart + fdt_totalsize(fdt);
407b908c675SJens Wiklander 	char *tmp;
408b908c675SJens Wiklander 
409*17f326ebSJerome Forissier 	FDT_RO_PROBE(fdt);
410b908c675SJens Wiklander 
411b908c675SJens Wiklander 	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
412b908c675SJens Wiklander 		* sizeof(struct fdt_reserve_entry);
413b908c675SJens Wiklander 
414b908c675SJens Wiklander 	if (fdt_version(fdt) >= 17) {
415b908c675SJens Wiklander 		struct_size = fdt_size_dt_struct(fdt);
416b908c675SJens Wiklander 	} else {
417b908c675SJens Wiklander 		struct_size = 0;
418b908c675SJens Wiklander 		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
419b908c675SJens Wiklander 			;
420b908c675SJens Wiklander 		if (struct_size < 0)
421b908c675SJens Wiklander 			return struct_size;
422b908c675SJens Wiklander 	}
423b908c675SJens Wiklander 
42401d6a9daSBryan O'Donoghue 	if (!fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
425b908c675SJens Wiklander 		/* no further work necessary */
426b908c675SJens Wiklander 		err = fdt_move(fdt, buf, bufsize);
427b908c675SJens Wiklander 		if (err)
428b908c675SJens Wiklander 			return err;
429b908c675SJens Wiklander 		fdt_set_version(buf, 17);
430b908c675SJens Wiklander 		fdt_set_size_dt_struct(buf, struct_size);
431b908c675SJens Wiklander 		fdt_set_totalsize(buf, bufsize);
432b908c675SJens Wiklander 		return 0;
433b908c675SJens Wiklander 	}
434b908c675SJens Wiklander 
435b908c675SJens Wiklander 	/* Need to reorder */
436b908c675SJens Wiklander 	newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
437b908c675SJens Wiklander 		+ struct_size + fdt_size_dt_strings(fdt);
438b908c675SJens Wiklander 
439b908c675SJens Wiklander 	if (bufsize < newsize)
440b908c675SJens Wiklander 		return -FDT_ERR_NOSPACE;
441b908c675SJens Wiklander 
442b908c675SJens Wiklander 	/* First attempt to build converted tree at beginning of buffer */
443b908c675SJens Wiklander 	tmp = buf;
444b908c675SJens Wiklander 	/* But if that overlaps with the old tree... */
445b908c675SJens Wiklander 	if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
446b908c675SJens Wiklander 		/* Try right after the old tree instead */
447b908c675SJens Wiklander 		tmp = (char *)(uintptr_t)fdtend;
448b908c675SJens Wiklander 		if ((tmp + newsize) > ((char *)buf + bufsize))
449b908c675SJens Wiklander 			return -FDT_ERR_NOSPACE;
450b908c675SJens Wiklander 	}
451b908c675SJens Wiklander 
45201d6a9daSBryan O'Donoghue 	fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size);
453b908c675SJens Wiklander 	memmove(buf, tmp, newsize);
454b908c675SJens Wiklander 
455b908c675SJens Wiklander 	fdt_set_magic(buf, FDT_MAGIC);
456b908c675SJens Wiklander 	fdt_set_totalsize(buf, bufsize);
457b908c675SJens Wiklander 	fdt_set_version(buf, 17);
458b908c675SJens Wiklander 	fdt_set_last_comp_version(buf, 16);
459b908c675SJens Wiklander 	fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
460b908c675SJens Wiklander 
461b908c675SJens Wiklander 	return 0;
462b908c675SJens Wiklander }
463b908c675SJens Wiklander 
fdt_pack(void * fdt)464b908c675SJens Wiklander int fdt_pack(void *fdt)
465b908c675SJens Wiklander {
466b908c675SJens Wiklander 	int mem_rsv_size;
467b908c675SJens Wiklander 
468*17f326ebSJerome Forissier 	FDT_RW_PROBE(fdt);
469b908c675SJens Wiklander 
470b908c675SJens Wiklander 	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
471b908c675SJens Wiklander 		* sizeof(struct fdt_reserve_entry);
47201d6a9daSBryan O'Donoghue 	fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
47301d6a9daSBryan O'Donoghue 	fdt_set_totalsize(fdt, fdt_data_size_(fdt));
474b908c675SJens Wiklander 
475b908c675SJens Wiklander 	return 0;
476b908c675SJens Wiklander }
477