xref: /linux/scripts/dtc/libfdt/fdt_sw.c (revision 9fffb55f66127b52c937ede5196ebfa0c0d50bce)
1*9fffb55fSDavid Gibson /*
2*9fffb55fSDavid Gibson  * libfdt - Flat Device Tree manipulation
3*9fffb55fSDavid Gibson  * Copyright (C) 2006 David Gibson, IBM Corporation.
4*9fffb55fSDavid Gibson  *
5*9fffb55fSDavid Gibson  * libfdt is dual licensed: you can use it either under the terms of
6*9fffb55fSDavid Gibson  * the GPL, or the BSD license, at your option.
7*9fffb55fSDavid Gibson  *
8*9fffb55fSDavid Gibson  *  a) This library is free software; you can redistribute it and/or
9*9fffb55fSDavid Gibson  *     modify it under the terms of the GNU General Public License as
10*9fffb55fSDavid Gibson  *     published by the Free Software Foundation; either version 2 of the
11*9fffb55fSDavid Gibson  *     License, or (at your option) any later version.
12*9fffb55fSDavid Gibson  *
13*9fffb55fSDavid Gibson  *     This library is distributed in the hope that it will be useful,
14*9fffb55fSDavid Gibson  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
15*9fffb55fSDavid Gibson  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*9fffb55fSDavid Gibson  *     GNU General Public License for more details.
17*9fffb55fSDavid Gibson  *
18*9fffb55fSDavid Gibson  *     You should have received a copy of the GNU General Public
19*9fffb55fSDavid Gibson  *     License along with this library; if not, write to the Free
20*9fffb55fSDavid Gibson  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21*9fffb55fSDavid Gibson  *     MA 02110-1301 USA
22*9fffb55fSDavid Gibson  *
23*9fffb55fSDavid Gibson  * Alternatively,
24*9fffb55fSDavid Gibson  *
25*9fffb55fSDavid Gibson  *  b) Redistribution and use in source and binary forms, with or
26*9fffb55fSDavid Gibson  *     without modification, are permitted provided that the following
27*9fffb55fSDavid Gibson  *     conditions are met:
28*9fffb55fSDavid Gibson  *
29*9fffb55fSDavid Gibson  *     1. Redistributions of source code must retain the above
30*9fffb55fSDavid Gibson  *        copyright notice, this list of conditions and the following
31*9fffb55fSDavid Gibson  *        disclaimer.
32*9fffb55fSDavid Gibson  *     2. Redistributions in binary form must reproduce the above
33*9fffb55fSDavid Gibson  *        copyright notice, this list of conditions and the following
34*9fffb55fSDavid Gibson  *        disclaimer in the documentation and/or other materials
35*9fffb55fSDavid Gibson  *        provided with the distribution.
36*9fffb55fSDavid Gibson  *
37*9fffb55fSDavid Gibson  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38*9fffb55fSDavid Gibson  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39*9fffb55fSDavid Gibson  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40*9fffb55fSDavid Gibson  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41*9fffb55fSDavid Gibson  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42*9fffb55fSDavid Gibson  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43*9fffb55fSDavid Gibson  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44*9fffb55fSDavid Gibson  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45*9fffb55fSDavid Gibson  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46*9fffb55fSDavid Gibson  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47*9fffb55fSDavid Gibson  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48*9fffb55fSDavid Gibson  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49*9fffb55fSDavid Gibson  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50*9fffb55fSDavid Gibson  */
51*9fffb55fSDavid Gibson #include "libfdt_env.h"
52*9fffb55fSDavid Gibson 
53*9fffb55fSDavid Gibson #include <fdt.h>
54*9fffb55fSDavid Gibson #include <libfdt.h>
55*9fffb55fSDavid Gibson 
56*9fffb55fSDavid Gibson #include "libfdt_internal.h"
57*9fffb55fSDavid Gibson 
58*9fffb55fSDavid Gibson static int _fdt_sw_check_header(void *fdt)
59*9fffb55fSDavid Gibson {
60*9fffb55fSDavid Gibson 	if (fdt_magic(fdt) != FDT_SW_MAGIC)
61*9fffb55fSDavid Gibson 		return -FDT_ERR_BADMAGIC;
62*9fffb55fSDavid Gibson 	/* FIXME: should check more details about the header state */
63*9fffb55fSDavid Gibson 	return 0;
64*9fffb55fSDavid Gibson }
65*9fffb55fSDavid Gibson 
66*9fffb55fSDavid Gibson #define FDT_SW_CHECK_HEADER(fdt) \
67*9fffb55fSDavid Gibson 	{ \
68*9fffb55fSDavid Gibson 		int err; \
69*9fffb55fSDavid Gibson 		if ((err = _fdt_sw_check_header(fdt)) != 0) \
70*9fffb55fSDavid Gibson 			return err; \
71*9fffb55fSDavid Gibson 	}
72*9fffb55fSDavid Gibson 
73*9fffb55fSDavid Gibson static void *_fdt_grab_space(void *fdt, int len)
74*9fffb55fSDavid Gibson {
75*9fffb55fSDavid Gibson 	int offset = fdt_size_dt_struct(fdt);
76*9fffb55fSDavid Gibson 	int spaceleft;
77*9fffb55fSDavid Gibson 
78*9fffb55fSDavid Gibson 	spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
79*9fffb55fSDavid Gibson 		- fdt_size_dt_strings(fdt);
80*9fffb55fSDavid Gibson 
81*9fffb55fSDavid Gibson 	if ((offset + len < offset) || (offset + len > spaceleft))
82*9fffb55fSDavid Gibson 		return NULL;
83*9fffb55fSDavid Gibson 
84*9fffb55fSDavid Gibson 	fdt_set_size_dt_struct(fdt, offset + len);
85*9fffb55fSDavid Gibson 	return fdt_offset_ptr_w(fdt, offset, len);
86*9fffb55fSDavid Gibson }
87*9fffb55fSDavid Gibson 
88*9fffb55fSDavid Gibson int fdt_create(void *buf, int bufsize)
89*9fffb55fSDavid Gibson {
90*9fffb55fSDavid Gibson 	void *fdt = buf;
91*9fffb55fSDavid Gibson 
92*9fffb55fSDavid Gibson 	if (bufsize < sizeof(struct fdt_header))
93*9fffb55fSDavid Gibson 		return -FDT_ERR_NOSPACE;
94*9fffb55fSDavid Gibson 
95*9fffb55fSDavid Gibson 	memset(buf, 0, bufsize);
96*9fffb55fSDavid Gibson 
97*9fffb55fSDavid Gibson 	fdt_set_magic(fdt, FDT_SW_MAGIC);
98*9fffb55fSDavid Gibson 	fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
99*9fffb55fSDavid Gibson 	fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
100*9fffb55fSDavid Gibson 	fdt_set_totalsize(fdt,  bufsize);
101*9fffb55fSDavid Gibson 
102*9fffb55fSDavid Gibson 	fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
103*9fffb55fSDavid Gibson 					      sizeof(struct fdt_reserve_entry)));
104*9fffb55fSDavid Gibson 	fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
105*9fffb55fSDavid Gibson 	fdt_set_off_dt_strings(fdt, bufsize);
106*9fffb55fSDavid Gibson 
107*9fffb55fSDavid Gibson 	return 0;
108*9fffb55fSDavid Gibson }
109*9fffb55fSDavid Gibson 
110*9fffb55fSDavid Gibson int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
111*9fffb55fSDavid Gibson {
112*9fffb55fSDavid Gibson 	struct fdt_reserve_entry *re;
113*9fffb55fSDavid Gibson 	int offset;
114*9fffb55fSDavid Gibson 
115*9fffb55fSDavid Gibson 	FDT_SW_CHECK_HEADER(fdt);
116*9fffb55fSDavid Gibson 
117*9fffb55fSDavid Gibson 	if (fdt_size_dt_struct(fdt))
118*9fffb55fSDavid Gibson 		return -FDT_ERR_BADSTATE;
119*9fffb55fSDavid Gibson 
120*9fffb55fSDavid Gibson 	offset = fdt_off_dt_struct(fdt);
121*9fffb55fSDavid Gibson 	if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
122*9fffb55fSDavid Gibson 		return -FDT_ERR_NOSPACE;
123*9fffb55fSDavid Gibson 
124*9fffb55fSDavid Gibson 	re = (struct fdt_reserve_entry *)((char *)fdt + offset);
125*9fffb55fSDavid Gibson 	re->address = cpu_to_fdt64(addr);
126*9fffb55fSDavid Gibson 	re->size = cpu_to_fdt64(size);
127*9fffb55fSDavid Gibson 
128*9fffb55fSDavid Gibson 	fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
129*9fffb55fSDavid Gibson 
130*9fffb55fSDavid Gibson 	return 0;
131*9fffb55fSDavid Gibson }
132*9fffb55fSDavid Gibson 
133*9fffb55fSDavid Gibson int fdt_finish_reservemap(void *fdt)
134*9fffb55fSDavid Gibson {
135*9fffb55fSDavid Gibson 	return fdt_add_reservemap_entry(fdt, 0, 0);
136*9fffb55fSDavid Gibson }
137*9fffb55fSDavid Gibson 
138*9fffb55fSDavid Gibson int fdt_begin_node(void *fdt, const char *name)
139*9fffb55fSDavid Gibson {
140*9fffb55fSDavid Gibson 	struct fdt_node_header *nh;
141*9fffb55fSDavid Gibson 	int namelen = strlen(name) + 1;
142*9fffb55fSDavid Gibson 
143*9fffb55fSDavid Gibson 	FDT_SW_CHECK_HEADER(fdt);
144*9fffb55fSDavid Gibson 
145*9fffb55fSDavid Gibson 	nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
146*9fffb55fSDavid Gibson 	if (! nh)
147*9fffb55fSDavid Gibson 		return -FDT_ERR_NOSPACE;
148*9fffb55fSDavid Gibson 
149*9fffb55fSDavid Gibson 	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
150*9fffb55fSDavid Gibson 	memcpy(nh->name, name, namelen);
151*9fffb55fSDavid Gibson 	return 0;
152*9fffb55fSDavid Gibson }
153*9fffb55fSDavid Gibson 
154*9fffb55fSDavid Gibson int fdt_end_node(void *fdt)
155*9fffb55fSDavid Gibson {
156*9fffb55fSDavid Gibson 	uint32_t *en;
157*9fffb55fSDavid Gibson 
158*9fffb55fSDavid Gibson 	FDT_SW_CHECK_HEADER(fdt);
159*9fffb55fSDavid Gibson 
160*9fffb55fSDavid Gibson 	en = _fdt_grab_space(fdt, FDT_TAGSIZE);
161*9fffb55fSDavid Gibson 	if (! en)
162*9fffb55fSDavid Gibson 		return -FDT_ERR_NOSPACE;
163*9fffb55fSDavid Gibson 
164*9fffb55fSDavid Gibson 	*en = cpu_to_fdt32(FDT_END_NODE);
165*9fffb55fSDavid Gibson 	return 0;
166*9fffb55fSDavid Gibson }
167*9fffb55fSDavid Gibson 
168*9fffb55fSDavid Gibson static int _fdt_find_add_string(void *fdt, const char *s)
169*9fffb55fSDavid Gibson {
170*9fffb55fSDavid Gibson 	char *strtab = (char *)fdt + fdt_totalsize(fdt);
171*9fffb55fSDavid Gibson 	const char *p;
172*9fffb55fSDavid Gibson 	int strtabsize = fdt_size_dt_strings(fdt);
173*9fffb55fSDavid Gibson 	int len = strlen(s) + 1;
174*9fffb55fSDavid Gibson 	int struct_top, offset;
175*9fffb55fSDavid Gibson 
176*9fffb55fSDavid Gibson 	p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
177*9fffb55fSDavid Gibson 	if (p)
178*9fffb55fSDavid Gibson 		return p - strtab;
179*9fffb55fSDavid Gibson 
180*9fffb55fSDavid Gibson 	/* Add it */
181*9fffb55fSDavid Gibson 	offset = -strtabsize - len;
182*9fffb55fSDavid Gibson 	struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
183*9fffb55fSDavid Gibson 	if (fdt_totalsize(fdt) + offset < struct_top)
184*9fffb55fSDavid Gibson 		return 0; /* no more room :( */
185*9fffb55fSDavid Gibson 
186*9fffb55fSDavid Gibson 	memcpy(strtab + offset, s, len);
187*9fffb55fSDavid Gibson 	fdt_set_size_dt_strings(fdt, strtabsize + len);
188*9fffb55fSDavid Gibson 	return offset;
189*9fffb55fSDavid Gibson }
190*9fffb55fSDavid Gibson 
191*9fffb55fSDavid Gibson int fdt_property(void *fdt, const char *name, const void *val, int len)
192*9fffb55fSDavid Gibson {
193*9fffb55fSDavid Gibson 	struct fdt_property *prop;
194*9fffb55fSDavid Gibson 	int nameoff;
195*9fffb55fSDavid Gibson 
196*9fffb55fSDavid Gibson 	FDT_SW_CHECK_HEADER(fdt);
197*9fffb55fSDavid Gibson 
198*9fffb55fSDavid Gibson 	nameoff = _fdt_find_add_string(fdt, name);
199*9fffb55fSDavid Gibson 	if (nameoff == 0)
200*9fffb55fSDavid Gibson 		return -FDT_ERR_NOSPACE;
201*9fffb55fSDavid Gibson 
202*9fffb55fSDavid Gibson 	prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
203*9fffb55fSDavid Gibson 	if (! prop)
204*9fffb55fSDavid Gibson 		return -FDT_ERR_NOSPACE;
205*9fffb55fSDavid Gibson 
206*9fffb55fSDavid Gibson 	prop->tag = cpu_to_fdt32(FDT_PROP);
207*9fffb55fSDavid Gibson 	prop->nameoff = cpu_to_fdt32(nameoff);
208*9fffb55fSDavid Gibson 	prop->len = cpu_to_fdt32(len);
209*9fffb55fSDavid Gibson 	memcpy(prop->data, val, len);
210*9fffb55fSDavid Gibson 	return 0;
211*9fffb55fSDavid Gibson }
212*9fffb55fSDavid Gibson 
213*9fffb55fSDavid Gibson int fdt_finish(void *fdt)
214*9fffb55fSDavid Gibson {
215*9fffb55fSDavid Gibson 	char *p = (char *)fdt;
216*9fffb55fSDavid Gibson 	uint32_t *end;
217*9fffb55fSDavid Gibson 	int oldstroffset, newstroffset;
218*9fffb55fSDavid Gibson 	uint32_t tag;
219*9fffb55fSDavid Gibson 	int offset, nextoffset;
220*9fffb55fSDavid Gibson 
221*9fffb55fSDavid Gibson 	FDT_SW_CHECK_HEADER(fdt);
222*9fffb55fSDavid Gibson 
223*9fffb55fSDavid Gibson 	/* Add terminator */
224*9fffb55fSDavid Gibson 	end = _fdt_grab_space(fdt, sizeof(*end));
225*9fffb55fSDavid Gibson 	if (! end)
226*9fffb55fSDavid Gibson 		return -FDT_ERR_NOSPACE;
227*9fffb55fSDavid Gibson 	*end = cpu_to_fdt32(FDT_END);
228*9fffb55fSDavid Gibson 
229*9fffb55fSDavid Gibson 	/* Relocate the string table */
230*9fffb55fSDavid Gibson 	oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
231*9fffb55fSDavid Gibson 	newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
232*9fffb55fSDavid Gibson 	memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
233*9fffb55fSDavid Gibson 	fdt_set_off_dt_strings(fdt, newstroffset);
234*9fffb55fSDavid Gibson 
235*9fffb55fSDavid Gibson 	/* Walk the structure, correcting string offsets */
236*9fffb55fSDavid Gibson 	offset = 0;
237*9fffb55fSDavid Gibson 	while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
238*9fffb55fSDavid Gibson 		if (tag == FDT_PROP) {
239*9fffb55fSDavid Gibson 			struct fdt_property *prop =
240*9fffb55fSDavid Gibson 				fdt_offset_ptr_w(fdt, offset, sizeof(*prop));
241*9fffb55fSDavid Gibson 			int nameoff;
242*9fffb55fSDavid Gibson 
243*9fffb55fSDavid Gibson 			if (! prop)
244*9fffb55fSDavid Gibson 				return -FDT_ERR_BADSTRUCTURE;
245*9fffb55fSDavid Gibson 
246*9fffb55fSDavid Gibson 			nameoff = fdt32_to_cpu(prop->nameoff);
247*9fffb55fSDavid Gibson 			nameoff += fdt_size_dt_strings(fdt);
248*9fffb55fSDavid Gibson 			prop->nameoff = cpu_to_fdt32(nameoff);
249*9fffb55fSDavid Gibson 		}
250*9fffb55fSDavid Gibson 		offset = nextoffset;
251*9fffb55fSDavid Gibson 	}
252*9fffb55fSDavid Gibson 
253*9fffb55fSDavid Gibson 	/* Finally, adjust the header */
254*9fffb55fSDavid Gibson 	fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
255*9fffb55fSDavid Gibson 	fdt_set_magic(fdt, FDT_MAGIC);
256*9fffb55fSDavid Gibson 	return 0;
257*9fffb55fSDavid Gibson }
258