fdt_sw.c revision 204433
169783Smsmith/*
269783Smsmith * libfdt - Flat Device Tree manipulation
369783Smsmith * Copyright (C) 2006 David Gibson, IBM Corporation.
469783Smsmith *
569783Smsmith * libfdt is dual licensed: you can use it either under the terms of
669783Smsmith * the GPL, or the BSD license, at your option.
769783Smsmith *
869783Smsmith *  a) This library is free software; you can redistribute it and/or
969783Smsmith *     modify it under the terms of the GNU General Public License as
1069783Smsmith *     published by the Free Software Foundation; either version 2 of the
1169783Smsmith *     License, or (at your option) any later version.
1269783Smsmith *
1369783Smsmith *     This library is distributed in the hope that it will be useful,
1469783Smsmith *     but WITHOUT ANY WARRANTY; without even the implied warranty of
1569783Smsmith *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1669783Smsmith *     GNU General Public License for more details.
1769783Smsmith *
1869783Smsmith *     You should have received a copy of the GNU General Public
1969783Smsmith *     License along with this library; if not, write to the Free
2069783Smsmith *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
2169783Smsmith *     MA 02110-1301 USA
2269783Smsmith *
2369783Smsmith * Alternatively,
2469783Smsmith *
2569783Smsmith *  b) Redistribution and use in source and binary forms, with or
2669783Smsmith *     without modification, are permitted provided that the following
2769783Smsmith *     conditions are met:
2869783Smsmith *
2969783Smsmith *     1. Redistributions of source code must retain the above
3069783Smsmith *        copyright notice, this list of conditions and the following
31119418Sobrien *        disclaimer.
32119418Sobrien *     2. Redistributions in binary form must reproduce the above
33119418Sobrien *        copyright notice, this list of conditions and the following
3469783Smsmith *        disclaimer in the documentation and/or other materials
3569783Smsmith *        provided with the distribution.
3669783Smsmith *
3769783Smsmith *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
3869783Smsmith *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
3969783Smsmith *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
4069783Smsmith *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
4169783Smsmith *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42107546Simp *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43107546Simp *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44106844Smdodd *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
4569783Smsmith *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
4669783Smsmith *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
4769783Smsmith *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48119285Simp *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49119285Simp *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50119285Simp */
5169783Smsmith#include "libfdt_env.h"
5269783Smsmith
5369783Smsmith#include <fdt.h>
5469783Smsmith#include <libfdt.h>
5569783Smsmith
5669783Smsmith#include "libfdt_internal.h"
5769783Smsmith
5869783Smsmithstatic int _fdt_sw_check_header(void *fdt)
5969783Smsmith{
6069783Smsmith	if (fdt_magic(fdt) != FDT_SW_MAGIC)
6169783Smsmith		return -FDT_ERR_BADMAGIC;
6269783Smsmith	/* FIXME: should check more details about the header state */
6369783Smsmith	return 0;
6469783Smsmith}
6569783Smsmith
6669783Smsmith#define FDT_SW_CHECK_HEADER(fdt) \
6769783Smsmith	{ \
6869783Smsmith		int err; \
6969783Smsmith		if ((err = _fdt_sw_check_header(fdt)) != 0) \
7069783Smsmith			return err; \
7169783Smsmith	}
7269783Smsmith
7369783Smsmithstatic void *_fdt_grab_space(void *fdt, size_t len)
7469783Smsmith{
7569783Smsmith	int offset = fdt_size_dt_struct(fdt);
7669783Smsmith	int spaceleft;
7769783Smsmith
7869783Smsmith	spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
7969783Smsmith		- fdt_size_dt_strings(fdt);
8069783Smsmith
8169783Smsmith	if ((offset + len < offset) || (offset + len > spaceleft))
8269783Smsmith		return NULL;
8369783Smsmith
8469783Smsmith	fdt_set_size_dt_struct(fdt, offset + len);
8569783Smsmith	return _fdt_offset_ptr_w(fdt, offset);
8669783Smsmith}
8769783Smsmith
8869783Smsmithint fdt_create(void *buf, int bufsize)
8969783Smsmith{
90102441Sjhb	void *fdt = buf;
9169783Smsmith
9269783Smsmith	if (bufsize < sizeof(struct fdt_header))
9369783Smsmith		return -FDT_ERR_NOSPACE;
9469783Smsmith
9569783Smsmith	memset(buf, 0, bufsize);
9669783Smsmith
9769783Smsmith	fdt_set_magic(fdt, FDT_SW_MAGIC);
9869783Smsmith	fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
9969783Smsmith	fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
10069783Smsmith	fdt_set_totalsize(fdt,  bufsize);
10169783Smsmith
10269783Smsmith	fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
10369783Smsmith					      sizeof(struct fdt_reserve_entry)));
10469783Smsmith	fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
10569783Smsmith	fdt_set_off_dt_strings(fdt, bufsize);
10669783Smsmith
10769783Smsmith	return 0;
108102441Sjhb}
109102441Sjhb
11069783Smsmithint fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
11169783Smsmith{
112119266Simp	struct fdt_reserve_entry *re;
11369783Smsmith	int offset;
11469783Smsmith
11569783Smsmith	FDT_SW_CHECK_HEADER(fdt);
11669783Smsmith
11769908Smsmith	if (fdt_size_dt_struct(fdt))
11869908Smsmith		return -FDT_ERR_BADSTATE;
11969908Smsmith
12069953Smsmith	offset = fdt_off_dt_struct(fdt);
12169908Smsmith	if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
12269908Smsmith		return -FDT_ERR_NOSPACE;
12369908Smsmith
12469908Smsmith	re = (struct fdt_reserve_entry *)((char *)fdt + offset);
12569908Smsmith	re->address = cpu_to_fdt64(addr);
12669783Smsmith	re->size = cpu_to_fdt64(size);
12769908Smsmith
12869908Smsmith	fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
12969908Smsmith
13069953Smsmith	return 0;
13169953Smsmith}
13269953Smsmith
13369953Smsmithint fdt_finish_reservemap(void *fdt)
13469953Smsmith{
13569953Smsmith	return fdt_add_reservemap_entry(fdt, 0, 0);
13669953Smsmith}
13769953Smsmith
13869908Smsmithint fdt_begin_node(void *fdt, const char *name)
13969953Smsmith{
14069953Smsmith	struct fdt_node_header *nh;
14169953Smsmith	int namelen = strlen(name) + 1;
14269953Smsmith
14369953Smsmith	FDT_SW_CHECK_HEADER(fdt);
14469953Smsmith
14569953Smsmith	nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
14669908Smsmith	if (! nh)
14769908Smsmith		return -FDT_ERR_NOSPACE;
14869908Smsmith
14969908Smsmith	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
15069908Smsmith	memcpy(nh->name, name, namelen);
15169953Smsmith	return 0;
15269953Smsmith}
15369953Smsmith
15469953Smsmithint fdt_end_node(void *fdt)
15569953Smsmith{
15669953Smsmith	uint32_t *en;
15769953Smsmith
15869953Smsmith	FDT_SW_CHECK_HEADER(fdt);
15969908Smsmith
16069908Smsmith	en = _fdt_grab_space(fdt, FDT_TAGSIZE);
16169908Smsmith	if (! en)
16269908Smsmith		return -FDT_ERR_NOSPACE;
16369908Smsmith
164124365Simp	*en = cpu_to_fdt32(FDT_END_NODE);
16569908Smsmith	return 0;
166119266Simp}
16769908Smsmith
16869908Smsmithstatic int _fdt_find_add_string(void *fdt, const char *s)
16969908Smsmith{
17069908Smsmith	char *strtab = (char *)fdt + fdt_totalsize(fdt);
17169908Smsmith	const char *p;
17269908Smsmith	int strtabsize = fdt_size_dt_strings(fdt);
173124365Simp	int len = strlen(s) + 1;
17469908Smsmith	int struct_top, offset;
175124365Simp
176124365Simp	p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
177124365Simp	if (p)
178124365Simp		return p - strtab;
179124365Simp
180124365Simp	/* Add it */
181124365Simp	offset = -strtabsize - len;
182124365Simp	struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
183124365Simp	if (fdt_totalsize(fdt) + offset < struct_top)
184124365Simp		return 0; /* no more room :( */
185124365Simp
18669908Smsmith	memcpy(strtab + offset, s, len);
18769908Smsmith	fdt_set_size_dt_strings(fdt, strtabsize + len);
18869908Smsmith	return offset;
189124365Simp}
190124365Simp
191124365Simpint fdt_property(void *fdt, const char *name, const void *val, int len)
192124365Simp{
193124365Simp	struct fdt_property *prop;
194124365Simp	int nameoff;
195124365Simp
196124365Simp	FDT_SW_CHECK_HEADER(fdt);
197124365Simp
198124365Simp	nameoff = _fdt_find_add_string(fdt, name);
199124365Simp	if (nameoff == 0)
200124365Simp		return -FDT_ERR_NOSPACE;
20169783Smsmith
20269783Smsmith	prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
20369783Smsmith	if (! prop)
20469783Smsmith		return -FDT_ERR_NOSPACE;
20569783Smsmith
20669783Smsmith	prop->tag = cpu_to_fdt32(FDT_PROP);
207124365Simp	prop->nameoff = cpu_to_fdt32(nameoff);
208124365Simp	prop->len = cpu_to_fdt32(len);
20969783Smsmith	memcpy(prop->data, val, len);
21069783Smsmith	return 0;
21169783Smsmith}
21269783Smsmith
21369783Smsmithint fdt_finish(void *fdt)
21469783Smsmith{
21569783Smsmith	char *p = (char *)fdt;
21669783Smsmith	uint32_t *end;
21769783Smsmith	int oldstroffset, newstroffset;
21869783Smsmith	uint32_t tag;
21969783Smsmith	int offset, nextoffset;
22069783Smsmith
22169783Smsmith	FDT_SW_CHECK_HEADER(fdt);
222102441Sjhb
22369783Smsmith	/* Add terminator */
224103042Sjhb	end = _fdt_grab_space(fdt, sizeof(*end));
225102441Sjhb	if (! end)
226102441Sjhb		return -FDT_ERR_NOSPACE;
227102441Sjhb	*end = cpu_to_fdt32(FDT_END);
228102441Sjhb
229102441Sjhb	/* Relocate the string table */
230102441Sjhb	oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
231102441Sjhb	newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
23269783Smsmith	memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
233103016Sjhb	fdt_set_off_dt_strings(fdt, newstroffset);
23469783Smsmith
23569783Smsmith	/* Walk the structure, correcting string offsets */
23669783Smsmith	offset = 0;
23769783Smsmith	while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
23869783Smsmith		if (tag == FDT_PROP) {
23969783Smsmith			struct fdt_property *prop =
24069783Smsmith				_fdt_offset_ptr_w(fdt, offset);
24169783Smsmith			int nameoff;
242102441Sjhb
24369783Smsmith			nameoff = fdt32_to_cpu(prop->nameoff);
24469783Smsmith			nameoff += fdt_size_dt_strings(fdt);
24569783Smsmith			prop->nameoff = cpu_to_fdt32(nameoff);
24669783Smsmith		}
24769783Smsmith		offset = nextoffset;
24869783Smsmith	}
24969783Smsmith	if (nextoffset < 0)
25069783Smsmith		return nextoffset;
25169783Smsmith
25269783Smsmith	/* Finally, adjust the header */
25369783Smsmith	fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
25469783Smsmith	fdt_set_magic(fdt, FDT_MAGIC);
255102441Sjhb	return 0;
25669783Smsmith}
25769783Smsmith