xref: /arm-trusted-firmware/lib/libfdt/fdt_rw.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu // SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
2*91f16700Schasinglulu /*
3*91f16700Schasinglulu  * libfdt - Flat Device Tree manipulation
4*91f16700Schasinglulu  * Copyright (C) 2006 David Gibson, IBM Corporation.
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu #include "libfdt_env.h"
7*91f16700Schasinglulu 
8*91f16700Schasinglulu #include <fdt.h>
9*91f16700Schasinglulu #include <libfdt.h>
10*91f16700Schasinglulu 
11*91f16700Schasinglulu #include "libfdt_internal.h"
12*91f16700Schasinglulu 
13*91f16700Schasinglulu static int fdt_blocks_misordered_(const void *fdt,
14*91f16700Schasinglulu 				  int mem_rsv_size, int struct_size)
15*91f16700Schasinglulu {
16*91f16700Schasinglulu 	return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
17*91f16700Schasinglulu 		|| (fdt_off_dt_struct(fdt) <
18*91f16700Schasinglulu 		    (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
19*91f16700Schasinglulu 		|| (fdt_off_dt_strings(fdt) <
20*91f16700Schasinglulu 		    (fdt_off_dt_struct(fdt) + struct_size))
21*91f16700Schasinglulu 		|| (fdt_totalsize(fdt) <
22*91f16700Schasinglulu 		    (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
23*91f16700Schasinglulu }
24*91f16700Schasinglulu 
25*91f16700Schasinglulu static int fdt_rw_probe_(void *fdt)
26*91f16700Schasinglulu {
27*91f16700Schasinglulu 	if (can_assume(VALID_DTB))
28*91f16700Schasinglulu 		return 0;
29*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
30*91f16700Schasinglulu 
31*91f16700Schasinglulu 	if (!can_assume(LATEST) && fdt_version(fdt) < 17)
32*91f16700Schasinglulu 		return -FDT_ERR_BADVERSION;
33*91f16700Schasinglulu 	if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
34*91f16700Schasinglulu 				   fdt_size_dt_struct(fdt)))
35*91f16700Schasinglulu 		return -FDT_ERR_BADLAYOUT;
36*91f16700Schasinglulu 	if (!can_assume(LATEST) && fdt_version(fdt) > 17)
37*91f16700Schasinglulu 		fdt_set_version(fdt, 17);
38*91f16700Schasinglulu 
39*91f16700Schasinglulu 	return 0;
40*91f16700Schasinglulu }
41*91f16700Schasinglulu 
42*91f16700Schasinglulu #define FDT_RW_PROBE(fdt) \
43*91f16700Schasinglulu 	{ \
44*91f16700Schasinglulu 		int err_; \
45*91f16700Schasinglulu 		if ((err_ = fdt_rw_probe_(fdt)) != 0) \
46*91f16700Schasinglulu 			return err_; \
47*91f16700Schasinglulu 	}
48*91f16700Schasinglulu 
49*91f16700Schasinglulu static inline unsigned int fdt_data_size_(void *fdt)
50*91f16700Schasinglulu {
51*91f16700Schasinglulu 	return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
52*91f16700Schasinglulu }
53*91f16700Schasinglulu 
54*91f16700Schasinglulu static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
55*91f16700Schasinglulu {
56*91f16700Schasinglulu 	char *p = splicepoint;
57*91f16700Schasinglulu 	unsigned int dsize = fdt_data_size_(fdt);
58*91f16700Schasinglulu 	size_t soff = p - (char *)fdt;
59*91f16700Schasinglulu 
60*91f16700Schasinglulu 	if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
61*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
62*91f16700Schasinglulu 	if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
63*91f16700Schasinglulu 		return -FDT_ERR_BADOFFSET;
64*91f16700Schasinglulu 	if (dsize - oldlen + newlen > fdt_totalsize(fdt))
65*91f16700Schasinglulu 		return -FDT_ERR_NOSPACE;
66*91f16700Schasinglulu 	memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
67*91f16700Schasinglulu 	return 0;
68*91f16700Schasinglulu }
69*91f16700Schasinglulu 
70*91f16700Schasinglulu static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
71*91f16700Schasinglulu 			       int oldn, int newn)
72*91f16700Schasinglulu {
73*91f16700Schasinglulu 	int delta = (newn - oldn) * sizeof(*p);
74*91f16700Schasinglulu 	int err;
75*91f16700Schasinglulu 	err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
76*91f16700Schasinglulu 	if (err)
77*91f16700Schasinglulu 		return err;
78*91f16700Schasinglulu 	fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
79*91f16700Schasinglulu 	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
80*91f16700Schasinglulu 	return 0;
81*91f16700Schasinglulu }
82*91f16700Schasinglulu 
83*91f16700Schasinglulu static int fdt_splice_struct_(void *fdt, void *p,
84*91f16700Schasinglulu 			      int oldlen, int newlen)
85*91f16700Schasinglulu {
86*91f16700Schasinglulu 	int delta = newlen - oldlen;
87*91f16700Schasinglulu 	int err;
88*91f16700Schasinglulu 
89*91f16700Schasinglulu 	if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
90*91f16700Schasinglulu 		return err;
91*91f16700Schasinglulu 
92*91f16700Schasinglulu 	fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
93*91f16700Schasinglulu 	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
94*91f16700Schasinglulu 	return 0;
95*91f16700Schasinglulu }
96*91f16700Schasinglulu 
97*91f16700Schasinglulu /* Must only be used to roll back in case of error */
98*91f16700Schasinglulu static void fdt_del_last_string_(void *fdt, const char *s)
99*91f16700Schasinglulu {
100*91f16700Schasinglulu 	int newlen = strlen(s) + 1;
101*91f16700Schasinglulu 
102*91f16700Schasinglulu 	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
103*91f16700Schasinglulu }
104*91f16700Schasinglulu 
105*91f16700Schasinglulu static int fdt_splice_string_(void *fdt, int newlen)
106*91f16700Schasinglulu {
107*91f16700Schasinglulu 	void *p = (char *)fdt
108*91f16700Schasinglulu 		+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
109*91f16700Schasinglulu 	int err;
110*91f16700Schasinglulu 
111*91f16700Schasinglulu 	if ((err = fdt_splice_(fdt, p, 0, newlen)))
112*91f16700Schasinglulu 		return err;
113*91f16700Schasinglulu 
114*91f16700Schasinglulu 	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
115*91f16700Schasinglulu 	return 0;
116*91f16700Schasinglulu }
117*91f16700Schasinglulu 
118*91f16700Schasinglulu /**
119*91f16700Schasinglulu  * fdt_find_add_string_() - Find or allocate a string
120*91f16700Schasinglulu  *
121*91f16700Schasinglulu  * @fdt: pointer to the device tree to check/adjust
122*91f16700Schasinglulu  * @s: string to find/add
123*91f16700Schasinglulu  * @allocated: Set to 0 if the string was found, 1 if not found and so
124*91f16700Schasinglulu  *	allocated. Ignored if can_assume(NO_ROLLBACK)
125*91f16700Schasinglulu  * @return offset of string in the string table (whether found or added)
126*91f16700Schasinglulu  */
127*91f16700Schasinglulu static int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
128*91f16700Schasinglulu {
129*91f16700Schasinglulu 	char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
130*91f16700Schasinglulu 	const char *p;
131*91f16700Schasinglulu 	char *new;
132*91f16700Schasinglulu 	int len = strlen(s) + 1;
133*91f16700Schasinglulu 	int err;
134*91f16700Schasinglulu 
135*91f16700Schasinglulu 	if (!can_assume(NO_ROLLBACK))
136*91f16700Schasinglulu 		*allocated = 0;
137*91f16700Schasinglulu 
138*91f16700Schasinglulu 	p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s);
139*91f16700Schasinglulu 	if (p)
140*91f16700Schasinglulu 		/* found it */
141*91f16700Schasinglulu 		return (p - strtab);
142*91f16700Schasinglulu 
143*91f16700Schasinglulu 	new = strtab + fdt_size_dt_strings(fdt);
144*91f16700Schasinglulu 	err = fdt_splice_string_(fdt, len);
145*91f16700Schasinglulu 	if (err)
146*91f16700Schasinglulu 		return err;
147*91f16700Schasinglulu 
148*91f16700Schasinglulu 	if (!can_assume(NO_ROLLBACK))
149*91f16700Schasinglulu 		*allocated = 1;
150*91f16700Schasinglulu 
151*91f16700Schasinglulu 	memcpy(new, s, len);
152*91f16700Schasinglulu 	return (new - strtab);
153*91f16700Schasinglulu }
154*91f16700Schasinglulu 
155*91f16700Schasinglulu int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
156*91f16700Schasinglulu {
157*91f16700Schasinglulu 	struct fdt_reserve_entry *re;
158*91f16700Schasinglulu 	int err;
159*91f16700Schasinglulu 
160*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
161*91f16700Schasinglulu 
162*91f16700Schasinglulu 	re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
163*91f16700Schasinglulu 	err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
164*91f16700Schasinglulu 	if (err)
165*91f16700Schasinglulu 		return err;
166*91f16700Schasinglulu 
167*91f16700Schasinglulu 	re->address = cpu_to_fdt64(address);
168*91f16700Schasinglulu 	re->size = cpu_to_fdt64(size);
169*91f16700Schasinglulu 	return 0;
170*91f16700Schasinglulu }
171*91f16700Schasinglulu 
172*91f16700Schasinglulu int fdt_del_mem_rsv(void *fdt, int n)
173*91f16700Schasinglulu {
174*91f16700Schasinglulu 	struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
175*91f16700Schasinglulu 
176*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
177*91f16700Schasinglulu 
178*91f16700Schasinglulu 	if (n >= fdt_num_mem_rsv(fdt))
179*91f16700Schasinglulu 		return -FDT_ERR_NOTFOUND;
180*91f16700Schasinglulu 
181*91f16700Schasinglulu 	return fdt_splice_mem_rsv_(fdt, re, 1, 0);
182*91f16700Schasinglulu }
183*91f16700Schasinglulu 
184*91f16700Schasinglulu static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
185*91f16700Schasinglulu 				int len, struct fdt_property **prop)
186*91f16700Schasinglulu {
187*91f16700Schasinglulu 	int oldlen;
188*91f16700Schasinglulu 	int err;
189*91f16700Schasinglulu 
190*91f16700Schasinglulu 	*prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
191*91f16700Schasinglulu 	if (!*prop)
192*91f16700Schasinglulu 		return oldlen;
193*91f16700Schasinglulu 
194*91f16700Schasinglulu 	if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
195*91f16700Schasinglulu 				      FDT_TAGALIGN(len))))
196*91f16700Schasinglulu 		return err;
197*91f16700Schasinglulu 
198*91f16700Schasinglulu 	(*prop)->len = cpu_to_fdt32(len);
199*91f16700Schasinglulu 	return 0;
200*91f16700Schasinglulu }
201*91f16700Schasinglulu 
202*91f16700Schasinglulu static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
203*91f16700Schasinglulu 			     int len, struct fdt_property **prop)
204*91f16700Schasinglulu {
205*91f16700Schasinglulu 	int proplen;
206*91f16700Schasinglulu 	int nextoffset;
207*91f16700Schasinglulu 	int namestroff;
208*91f16700Schasinglulu 	int err;
209*91f16700Schasinglulu 	int allocated;
210*91f16700Schasinglulu 
211*91f16700Schasinglulu 	if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
212*91f16700Schasinglulu 		return nextoffset;
213*91f16700Schasinglulu 
214*91f16700Schasinglulu 	namestroff = fdt_find_add_string_(fdt, name, &allocated);
215*91f16700Schasinglulu 	if (namestroff < 0)
216*91f16700Schasinglulu 		return namestroff;
217*91f16700Schasinglulu 
218*91f16700Schasinglulu 	*prop = fdt_offset_ptr_w_(fdt, nextoffset);
219*91f16700Schasinglulu 	proplen = sizeof(**prop) + FDT_TAGALIGN(len);
220*91f16700Schasinglulu 
221*91f16700Schasinglulu 	err = fdt_splice_struct_(fdt, *prop, 0, proplen);
222*91f16700Schasinglulu 	if (err) {
223*91f16700Schasinglulu 		/* Delete the string if we failed to add it */
224*91f16700Schasinglulu 		if (!can_assume(NO_ROLLBACK) && allocated)
225*91f16700Schasinglulu 			fdt_del_last_string_(fdt, name);
226*91f16700Schasinglulu 		return err;
227*91f16700Schasinglulu 	}
228*91f16700Schasinglulu 
229*91f16700Schasinglulu 	(*prop)->tag = cpu_to_fdt32(FDT_PROP);
230*91f16700Schasinglulu 	(*prop)->nameoff = cpu_to_fdt32(namestroff);
231*91f16700Schasinglulu 	(*prop)->len = cpu_to_fdt32(len);
232*91f16700Schasinglulu 	return 0;
233*91f16700Schasinglulu }
234*91f16700Schasinglulu 
235*91f16700Schasinglulu int fdt_set_name(void *fdt, int nodeoffset, const char *name)
236*91f16700Schasinglulu {
237*91f16700Schasinglulu 	char *namep;
238*91f16700Schasinglulu 	int oldlen, newlen;
239*91f16700Schasinglulu 	int err;
240*91f16700Schasinglulu 
241*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
242*91f16700Schasinglulu 
243*91f16700Schasinglulu 	namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
244*91f16700Schasinglulu 	if (!namep)
245*91f16700Schasinglulu 		return oldlen;
246*91f16700Schasinglulu 
247*91f16700Schasinglulu 	newlen = strlen(name);
248*91f16700Schasinglulu 
249*91f16700Schasinglulu 	err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
250*91f16700Schasinglulu 				 FDT_TAGALIGN(newlen+1));
251*91f16700Schasinglulu 	if (err)
252*91f16700Schasinglulu 		return err;
253*91f16700Schasinglulu 
254*91f16700Schasinglulu 	memcpy(namep, name, newlen+1);
255*91f16700Schasinglulu 	return 0;
256*91f16700Schasinglulu }
257*91f16700Schasinglulu 
258*91f16700Schasinglulu int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
259*91f16700Schasinglulu 			    int len, void **prop_data)
260*91f16700Schasinglulu {
261*91f16700Schasinglulu 	struct fdt_property *prop;
262*91f16700Schasinglulu 	int err;
263*91f16700Schasinglulu 
264*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
265*91f16700Schasinglulu 
266*91f16700Schasinglulu 	err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop);
267*91f16700Schasinglulu 	if (err == -FDT_ERR_NOTFOUND)
268*91f16700Schasinglulu 		err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
269*91f16700Schasinglulu 	if (err)
270*91f16700Schasinglulu 		return err;
271*91f16700Schasinglulu 
272*91f16700Schasinglulu 	*prop_data = prop->data;
273*91f16700Schasinglulu 	return 0;
274*91f16700Schasinglulu }
275*91f16700Schasinglulu 
276*91f16700Schasinglulu int fdt_setprop(void *fdt, int nodeoffset, const char *name,
277*91f16700Schasinglulu 		const void *val, int len)
278*91f16700Schasinglulu {
279*91f16700Schasinglulu 	void *prop_data;
280*91f16700Schasinglulu 	int err;
281*91f16700Schasinglulu 
282*91f16700Schasinglulu 	err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data);
283*91f16700Schasinglulu 	if (err)
284*91f16700Schasinglulu 		return err;
285*91f16700Schasinglulu 
286*91f16700Schasinglulu 	if (len)
287*91f16700Schasinglulu 		memcpy(prop_data, val, len);
288*91f16700Schasinglulu 	return 0;
289*91f16700Schasinglulu }
290*91f16700Schasinglulu 
291*91f16700Schasinglulu int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
292*91f16700Schasinglulu 		   const void *val, int len)
293*91f16700Schasinglulu {
294*91f16700Schasinglulu 	struct fdt_property *prop;
295*91f16700Schasinglulu 	int err, oldlen, newlen;
296*91f16700Schasinglulu 
297*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
298*91f16700Schasinglulu 
299*91f16700Schasinglulu 	prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
300*91f16700Schasinglulu 	if (prop) {
301*91f16700Schasinglulu 		newlen = len + oldlen;
302*91f16700Schasinglulu 		err = fdt_splice_struct_(fdt, prop->data,
303*91f16700Schasinglulu 					 FDT_TAGALIGN(oldlen),
304*91f16700Schasinglulu 					 FDT_TAGALIGN(newlen));
305*91f16700Schasinglulu 		if (err)
306*91f16700Schasinglulu 			return err;
307*91f16700Schasinglulu 		prop->len = cpu_to_fdt32(newlen);
308*91f16700Schasinglulu 		memcpy(prop->data + oldlen, val, len);
309*91f16700Schasinglulu 	} else {
310*91f16700Schasinglulu 		err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
311*91f16700Schasinglulu 		if (err)
312*91f16700Schasinglulu 			return err;
313*91f16700Schasinglulu 		memcpy(prop->data, val, len);
314*91f16700Schasinglulu 	}
315*91f16700Schasinglulu 	return 0;
316*91f16700Schasinglulu }
317*91f16700Schasinglulu 
318*91f16700Schasinglulu int fdt_delprop(void *fdt, int nodeoffset, const char *name)
319*91f16700Schasinglulu {
320*91f16700Schasinglulu 	struct fdt_property *prop;
321*91f16700Schasinglulu 	int len, proplen;
322*91f16700Schasinglulu 
323*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
324*91f16700Schasinglulu 
325*91f16700Schasinglulu 	prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
326*91f16700Schasinglulu 	if (!prop)
327*91f16700Schasinglulu 		return len;
328*91f16700Schasinglulu 
329*91f16700Schasinglulu 	proplen = sizeof(*prop) + FDT_TAGALIGN(len);
330*91f16700Schasinglulu 	return fdt_splice_struct_(fdt, prop, proplen, 0);
331*91f16700Schasinglulu }
332*91f16700Schasinglulu 
333*91f16700Schasinglulu int fdt_add_subnode_namelen(void *fdt, int parentoffset,
334*91f16700Schasinglulu 			    const char *name, int namelen)
335*91f16700Schasinglulu {
336*91f16700Schasinglulu 	struct fdt_node_header *nh;
337*91f16700Schasinglulu 	int offset, nextoffset;
338*91f16700Schasinglulu 	int nodelen;
339*91f16700Schasinglulu 	int err;
340*91f16700Schasinglulu 	uint32_t tag;
341*91f16700Schasinglulu 	fdt32_t *endtag;
342*91f16700Schasinglulu 
343*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
344*91f16700Schasinglulu 
345*91f16700Schasinglulu 	offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
346*91f16700Schasinglulu 	if (offset >= 0)
347*91f16700Schasinglulu 		return -FDT_ERR_EXISTS;
348*91f16700Schasinglulu 	else if (offset != -FDT_ERR_NOTFOUND)
349*91f16700Schasinglulu 		return offset;
350*91f16700Schasinglulu 
351*91f16700Schasinglulu 	/* Try to place the new node after the parent's properties */
352*91f16700Schasinglulu 	tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
353*91f16700Schasinglulu 	/* the fdt_subnode_offset_namelen() should ensure this never hits */
354*91f16700Schasinglulu 	if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
355*91f16700Schasinglulu 		return -FDT_ERR_INTERNAL;
356*91f16700Schasinglulu 	do {
357*91f16700Schasinglulu 		offset = nextoffset;
358*91f16700Schasinglulu 		tag = fdt_next_tag(fdt, offset, &nextoffset);
359*91f16700Schasinglulu 	} while ((tag == FDT_PROP) || (tag == FDT_NOP));
360*91f16700Schasinglulu 
361*91f16700Schasinglulu 	nh = fdt_offset_ptr_w_(fdt, offset);
362*91f16700Schasinglulu 	nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
363*91f16700Schasinglulu 
364*91f16700Schasinglulu 	err = fdt_splice_struct_(fdt, nh, 0, nodelen);
365*91f16700Schasinglulu 	if (err)
366*91f16700Schasinglulu 		return err;
367*91f16700Schasinglulu 
368*91f16700Schasinglulu 	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
369*91f16700Schasinglulu 	memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
370*91f16700Schasinglulu 	memcpy(nh->name, name, namelen);
371*91f16700Schasinglulu 	endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
372*91f16700Schasinglulu 	*endtag = cpu_to_fdt32(FDT_END_NODE);
373*91f16700Schasinglulu 
374*91f16700Schasinglulu 	return offset;
375*91f16700Schasinglulu }
376*91f16700Schasinglulu 
377*91f16700Schasinglulu int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
378*91f16700Schasinglulu {
379*91f16700Schasinglulu 	return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
380*91f16700Schasinglulu }
381*91f16700Schasinglulu 
382*91f16700Schasinglulu int fdt_del_node(void *fdt, int nodeoffset)
383*91f16700Schasinglulu {
384*91f16700Schasinglulu 	int endoffset;
385*91f16700Schasinglulu 
386*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
387*91f16700Schasinglulu 
388*91f16700Schasinglulu 	endoffset = fdt_node_end_offset_(fdt, nodeoffset);
389*91f16700Schasinglulu 	if (endoffset < 0)
390*91f16700Schasinglulu 		return endoffset;
391*91f16700Schasinglulu 
392*91f16700Schasinglulu 	return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
393*91f16700Schasinglulu 				  endoffset - nodeoffset, 0);
394*91f16700Schasinglulu }
395*91f16700Schasinglulu 
396*91f16700Schasinglulu static void fdt_packblocks_(const char *old, char *new,
397*91f16700Schasinglulu 			    int mem_rsv_size,
398*91f16700Schasinglulu 			    int struct_size,
399*91f16700Schasinglulu 			    int strings_size)
400*91f16700Schasinglulu {
401*91f16700Schasinglulu 	int mem_rsv_off, struct_off, strings_off;
402*91f16700Schasinglulu 
403*91f16700Schasinglulu 	mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
404*91f16700Schasinglulu 	struct_off = mem_rsv_off + mem_rsv_size;
405*91f16700Schasinglulu 	strings_off = struct_off + struct_size;
406*91f16700Schasinglulu 
407*91f16700Schasinglulu 	memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
408*91f16700Schasinglulu 	fdt_set_off_mem_rsvmap(new, mem_rsv_off);
409*91f16700Schasinglulu 
410*91f16700Schasinglulu 	memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
411*91f16700Schasinglulu 	fdt_set_off_dt_struct(new, struct_off);
412*91f16700Schasinglulu 	fdt_set_size_dt_struct(new, struct_size);
413*91f16700Schasinglulu 
414*91f16700Schasinglulu 	memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
415*91f16700Schasinglulu 	fdt_set_off_dt_strings(new, strings_off);
416*91f16700Schasinglulu 	fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
417*91f16700Schasinglulu }
418*91f16700Schasinglulu 
419*91f16700Schasinglulu int fdt_open_into(const void *fdt, void *buf, int bufsize)
420*91f16700Schasinglulu {
421*91f16700Schasinglulu 	int err;
422*91f16700Schasinglulu 	int mem_rsv_size, struct_size;
423*91f16700Schasinglulu 	int newsize;
424*91f16700Schasinglulu 	const char *fdtstart = fdt;
425*91f16700Schasinglulu 	const char *fdtend = fdtstart + fdt_totalsize(fdt);
426*91f16700Schasinglulu 	char *tmp;
427*91f16700Schasinglulu 
428*91f16700Schasinglulu 	FDT_RO_PROBE(fdt);
429*91f16700Schasinglulu 
430*91f16700Schasinglulu 	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
431*91f16700Schasinglulu 		* sizeof(struct fdt_reserve_entry);
432*91f16700Schasinglulu 
433*91f16700Schasinglulu 	if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
434*91f16700Schasinglulu 		struct_size = fdt_size_dt_struct(fdt);
435*91f16700Schasinglulu 	} else if (fdt_version(fdt) == 16) {
436*91f16700Schasinglulu 		struct_size = 0;
437*91f16700Schasinglulu 		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
438*91f16700Schasinglulu 			;
439*91f16700Schasinglulu 		if (struct_size < 0)
440*91f16700Schasinglulu 			return struct_size;
441*91f16700Schasinglulu 	} else {
442*91f16700Schasinglulu 		return -FDT_ERR_BADVERSION;
443*91f16700Schasinglulu 	}
444*91f16700Schasinglulu 
445*91f16700Schasinglulu 	if (can_assume(LIBFDT_ORDER) ||
446*91f16700Schasinglulu 	    !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
447*91f16700Schasinglulu 		/* no further work necessary */
448*91f16700Schasinglulu 		err = fdt_move(fdt, buf, bufsize);
449*91f16700Schasinglulu 		if (err)
450*91f16700Schasinglulu 			return err;
451*91f16700Schasinglulu 		fdt_set_version(buf, 17);
452*91f16700Schasinglulu 		fdt_set_size_dt_struct(buf, struct_size);
453*91f16700Schasinglulu 		fdt_set_totalsize(buf, bufsize);
454*91f16700Schasinglulu 		return 0;
455*91f16700Schasinglulu 	}
456*91f16700Schasinglulu 
457*91f16700Schasinglulu 	/* Need to reorder */
458*91f16700Schasinglulu 	newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
459*91f16700Schasinglulu 		+ struct_size + fdt_size_dt_strings(fdt);
460*91f16700Schasinglulu 
461*91f16700Schasinglulu 	if (bufsize < newsize)
462*91f16700Schasinglulu 		return -FDT_ERR_NOSPACE;
463*91f16700Schasinglulu 
464*91f16700Schasinglulu 	/* First attempt to build converted tree at beginning of buffer */
465*91f16700Schasinglulu 	tmp = buf;
466*91f16700Schasinglulu 	/* But if that overlaps with the old tree... */
467*91f16700Schasinglulu 	if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
468*91f16700Schasinglulu 		/* Try right after the old tree instead */
469*91f16700Schasinglulu 		tmp = (char *)(uintptr_t)fdtend;
470*91f16700Schasinglulu 		if ((tmp + newsize) > ((char *)buf + bufsize))
471*91f16700Schasinglulu 			return -FDT_ERR_NOSPACE;
472*91f16700Schasinglulu 	}
473*91f16700Schasinglulu 
474*91f16700Schasinglulu 	fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
475*91f16700Schasinglulu 			fdt_size_dt_strings(fdt));
476*91f16700Schasinglulu 	memmove(buf, tmp, newsize);
477*91f16700Schasinglulu 
478*91f16700Schasinglulu 	fdt_set_magic(buf, FDT_MAGIC);
479*91f16700Schasinglulu 	fdt_set_totalsize(buf, bufsize);
480*91f16700Schasinglulu 	fdt_set_version(buf, 17);
481*91f16700Schasinglulu 	fdt_set_last_comp_version(buf, 16);
482*91f16700Schasinglulu 	fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
483*91f16700Schasinglulu 
484*91f16700Schasinglulu 	return 0;
485*91f16700Schasinglulu }
486*91f16700Schasinglulu 
487*91f16700Schasinglulu int fdt_pack(void *fdt)
488*91f16700Schasinglulu {
489*91f16700Schasinglulu 	int mem_rsv_size;
490*91f16700Schasinglulu 
491*91f16700Schasinglulu 	FDT_RW_PROBE(fdt);
492*91f16700Schasinglulu 
493*91f16700Schasinglulu 	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
494*91f16700Schasinglulu 		* sizeof(struct fdt_reserve_entry);
495*91f16700Schasinglulu 	fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
496*91f16700Schasinglulu 			fdt_size_dt_strings(fdt));
497*91f16700Schasinglulu 	fdt_set_totalsize(fdt, fdt_data_size_(fdt));
498*91f16700Schasinglulu 
499*91f16700Schasinglulu 	return 0;
500*91f16700Schasinglulu }
501