162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * libfdt - Flat Device Tree manipulation
462306a36Sopenharmony_ci * Copyright (C) 2006 David Gibson, IBM Corporation.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "libfdt_env.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <fdt.h>
962306a36Sopenharmony_ci#include <libfdt.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "libfdt_internal.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic int fdt_blocks_misordered_(const void *fdt,
1462306a36Sopenharmony_ci				  int mem_rsv_size, int struct_size)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
1762306a36Sopenharmony_ci		|| (fdt_off_dt_struct(fdt) <
1862306a36Sopenharmony_ci		    (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
1962306a36Sopenharmony_ci		|| (fdt_off_dt_strings(fdt) <
2062306a36Sopenharmony_ci		    (fdt_off_dt_struct(fdt) + struct_size))
2162306a36Sopenharmony_ci		|| (fdt_totalsize(fdt) <
2262306a36Sopenharmony_ci		    (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int fdt_rw_probe_(void *fdt)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	if (can_assume(VALID_DTB))
2862306a36Sopenharmony_ci		return 0;
2962306a36Sopenharmony_ci	FDT_RO_PROBE(fdt);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (!can_assume(LATEST) && fdt_version(fdt) < 17)
3262306a36Sopenharmony_ci		return -FDT_ERR_BADVERSION;
3362306a36Sopenharmony_ci	if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),
3462306a36Sopenharmony_ci				   fdt_size_dt_struct(fdt)))
3562306a36Sopenharmony_ci		return -FDT_ERR_BADLAYOUT;
3662306a36Sopenharmony_ci	if (!can_assume(LATEST) && fdt_version(fdt) > 17)
3762306a36Sopenharmony_ci		fdt_set_version(fdt, 17);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return 0;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define FDT_RW_PROBE(fdt) \
4362306a36Sopenharmony_ci	{ \
4462306a36Sopenharmony_ci		int err_; \
4562306a36Sopenharmony_ci		if ((err_ = fdt_rw_probe_(fdt)) != 0) \
4662306a36Sopenharmony_ci			return err_; \
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic inline unsigned int fdt_data_size_(void *fdt)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	char *p = splicepoint;
5762306a36Sopenharmony_ci	unsigned int dsize = fdt_data_size_(fdt);
5862306a36Sopenharmony_ci	size_t soff = p - (char *)fdt;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))
6162306a36Sopenharmony_ci		return -FDT_ERR_BADOFFSET;
6262306a36Sopenharmony_ci	if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))
6362306a36Sopenharmony_ci		return -FDT_ERR_BADOFFSET;
6462306a36Sopenharmony_ci	if (dsize - oldlen + newlen > fdt_totalsize(fdt))
6562306a36Sopenharmony_ci		return -FDT_ERR_NOSPACE;
6662306a36Sopenharmony_ci	memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,
7162306a36Sopenharmony_ci			       int oldn, int newn)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int delta = (newn - oldn) * sizeof(*p);
7462306a36Sopenharmony_ci	int err;
7562306a36Sopenharmony_ci	err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
7662306a36Sopenharmony_ci	if (err)
7762306a36Sopenharmony_ci		return err;
7862306a36Sopenharmony_ci	fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
7962306a36Sopenharmony_ci	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int fdt_splice_struct_(void *fdt, void *p,
8462306a36Sopenharmony_ci			      int oldlen, int newlen)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int delta = newlen - oldlen;
8762306a36Sopenharmony_ci	int err;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if ((err = fdt_splice_(fdt, p, oldlen, newlen)))
9062306a36Sopenharmony_ci		return err;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
9362306a36Sopenharmony_ci	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* Must only be used to roll back in case of error */
9862306a36Sopenharmony_cistatic void fdt_del_last_string_(void *fdt, const char *s)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	int newlen = strlen(s) + 1;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic int fdt_splice_string_(void *fdt, int newlen)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	void *p = (char *)fdt
10862306a36Sopenharmony_ci		+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
10962306a36Sopenharmony_ci	int err;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if ((err = fdt_splice_(fdt, p, 0, newlen)))
11262306a36Sopenharmony_ci		return err;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
11562306a36Sopenharmony_ci	return 0;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/**
11962306a36Sopenharmony_ci * fdt_find_add_string_() - Find or allocate a string
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci * @fdt: pointer to the device tree to check/adjust
12262306a36Sopenharmony_ci * @s: string to find/add
12362306a36Sopenharmony_ci * @allocated: Set to 0 if the string was found, 1 if not found and so
12462306a36Sopenharmony_ci *	allocated. Ignored if can_assume(NO_ROLLBACK)
12562306a36Sopenharmony_ci * @return offset of string in the string table (whether found or added)
12662306a36Sopenharmony_ci */
12762306a36Sopenharmony_cistatic int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
13062306a36Sopenharmony_ci	const char *p;
13162306a36Sopenharmony_ci	char *new;
13262306a36Sopenharmony_ci	int len = strlen(s) + 1;
13362306a36Sopenharmony_ci	int err;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (!can_assume(NO_ROLLBACK))
13662306a36Sopenharmony_ci		*allocated = 0;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	p = fdt_find_string_(strtab, fdt_size_dt_strings(fdt), s);
13962306a36Sopenharmony_ci	if (p)
14062306a36Sopenharmony_ci		/* found it */
14162306a36Sopenharmony_ci		return (p - strtab);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	new = strtab + fdt_size_dt_strings(fdt);
14462306a36Sopenharmony_ci	err = fdt_splice_string_(fdt, len);
14562306a36Sopenharmony_ci	if (err)
14662306a36Sopenharmony_ci		return err;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (!can_assume(NO_ROLLBACK))
14962306a36Sopenharmony_ci		*allocated = 1;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	memcpy(new, s, len);
15262306a36Sopenharmony_ci	return (new - strtab);
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciint fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct fdt_reserve_entry *re;
15862306a36Sopenharmony_ci	int err;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));
16362306a36Sopenharmony_ci	err = fdt_splice_mem_rsv_(fdt, re, 0, 1);
16462306a36Sopenharmony_ci	if (err)
16562306a36Sopenharmony_ci		return err;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	re->address = cpu_to_fdt64(address);
16862306a36Sopenharmony_ci	re->size = cpu_to_fdt64(size);
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ciint fdt_del_mem_rsv(void *fdt, int n)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (n >= fdt_num_mem_rsv(fdt))
17962306a36Sopenharmony_ci		return -FDT_ERR_NOTFOUND;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return fdt_splice_mem_rsv_(fdt, re, 1, 0);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
18562306a36Sopenharmony_ci				int len, struct fdt_property **prop)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	int oldlen;
18862306a36Sopenharmony_ci	int err;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	*prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
19162306a36Sopenharmony_ci	if (!*prop)
19262306a36Sopenharmony_ci		return oldlen;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
19562306a36Sopenharmony_ci				      FDT_TAGALIGN(len))))
19662306a36Sopenharmony_ci		return err;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	(*prop)->len = cpu_to_fdt32(len);
19962306a36Sopenharmony_ci	return 0;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
20362306a36Sopenharmony_ci			     int len, struct fdt_property **prop)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int proplen;
20662306a36Sopenharmony_ci	int nextoffset;
20762306a36Sopenharmony_ci	int namestroff;
20862306a36Sopenharmony_ci	int err;
20962306a36Sopenharmony_ci	int allocated;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
21262306a36Sopenharmony_ci		return nextoffset;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	namestroff = fdt_find_add_string_(fdt, name, &allocated);
21562306a36Sopenharmony_ci	if (namestroff < 0)
21662306a36Sopenharmony_ci		return namestroff;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	*prop = fdt_offset_ptr_w_(fdt, nextoffset);
21962306a36Sopenharmony_ci	proplen = sizeof(**prop) + FDT_TAGALIGN(len);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	err = fdt_splice_struct_(fdt, *prop, 0, proplen);
22262306a36Sopenharmony_ci	if (err) {
22362306a36Sopenharmony_ci		/* Delete the string if we failed to add it */
22462306a36Sopenharmony_ci		if (!can_assume(NO_ROLLBACK) && allocated)
22562306a36Sopenharmony_ci			fdt_del_last_string_(fdt, name);
22662306a36Sopenharmony_ci		return err;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	(*prop)->tag = cpu_to_fdt32(FDT_PROP);
23062306a36Sopenharmony_ci	(*prop)->nameoff = cpu_to_fdt32(namestroff);
23162306a36Sopenharmony_ci	(*prop)->len = cpu_to_fdt32(len);
23262306a36Sopenharmony_ci	return 0;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ciint fdt_set_name(void *fdt, int nodeoffset, const char *name)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	char *namep;
23862306a36Sopenharmony_ci	int oldlen, newlen;
23962306a36Sopenharmony_ci	int err;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
24462306a36Sopenharmony_ci	if (!namep)
24562306a36Sopenharmony_ci		return oldlen;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	newlen = strlen(name);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),
25062306a36Sopenharmony_ci				 FDT_TAGALIGN(newlen+1));
25162306a36Sopenharmony_ci	if (err)
25262306a36Sopenharmony_ci		return err;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	memcpy(namep, name, newlen+1);
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciint fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name,
25962306a36Sopenharmony_ci			    int len, void **prop_data)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct fdt_property *prop;
26262306a36Sopenharmony_ci	int err;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	err = fdt_resize_property_(fdt, nodeoffset, name, len, &prop);
26762306a36Sopenharmony_ci	if (err == -FDT_ERR_NOTFOUND)
26862306a36Sopenharmony_ci		err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
26962306a36Sopenharmony_ci	if (err)
27062306a36Sopenharmony_ci		return err;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	*prop_data = prop->data;
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ciint fdt_setprop(void *fdt, int nodeoffset, const char *name,
27762306a36Sopenharmony_ci		const void *val, int len)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	void *prop_data;
28062306a36Sopenharmony_ci	int err;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data);
28362306a36Sopenharmony_ci	if (err)
28462306a36Sopenharmony_ci		return err;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (len)
28762306a36Sopenharmony_ci		memcpy(prop_data, val, len);
28862306a36Sopenharmony_ci	return 0;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ciint fdt_appendprop(void *fdt, int nodeoffset, const char *name,
29262306a36Sopenharmony_ci		   const void *val, int len)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct fdt_property *prop;
29562306a36Sopenharmony_ci	int err, oldlen, newlen;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
30062306a36Sopenharmony_ci	if (prop) {
30162306a36Sopenharmony_ci		newlen = len + oldlen;
30262306a36Sopenharmony_ci		err = fdt_splice_struct_(fdt, prop->data,
30362306a36Sopenharmony_ci					 FDT_TAGALIGN(oldlen),
30462306a36Sopenharmony_ci					 FDT_TAGALIGN(newlen));
30562306a36Sopenharmony_ci		if (err)
30662306a36Sopenharmony_ci			return err;
30762306a36Sopenharmony_ci		prop->len = cpu_to_fdt32(newlen);
30862306a36Sopenharmony_ci		memcpy(prop->data + oldlen, val, len);
30962306a36Sopenharmony_ci	} else {
31062306a36Sopenharmony_ci		err = fdt_add_property_(fdt, nodeoffset, name, len, &prop);
31162306a36Sopenharmony_ci		if (err)
31262306a36Sopenharmony_ci			return err;
31362306a36Sopenharmony_ci		memcpy(prop->data, val, len);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciint fdt_delprop(void *fdt, int nodeoffset, const char *name)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct fdt_property *prop;
32162306a36Sopenharmony_ci	int len, proplen;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
32662306a36Sopenharmony_ci	if (!prop)
32762306a36Sopenharmony_ci		return len;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	proplen = sizeof(*prop) + FDT_TAGALIGN(len);
33062306a36Sopenharmony_ci	return fdt_splice_struct_(fdt, prop, proplen, 0);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ciint fdt_add_subnode_namelen(void *fdt, int parentoffset,
33462306a36Sopenharmony_ci			    const char *name, int namelen)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct fdt_node_header *nh;
33762306a36Sopenharmony_ci	int offset, nextoffset;
33862306a36Sopenharmony_ci	int nodelen;
33962306a36Sopenharmony_ci	int err;
34062306a36Sopenharmony_ci	uint32_t tag;
34162306a36Sopenharmony_ci	fdt32_t *endtag;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
34662306a36Sopenharmony_ci	if (offset >= 0)
34762306a36Sopenharmony_ci		return -FDT_ERR_EXISTS;
34862306a36Sopenharmony_ci	else if (offset != -FDT_ERR_NOTFOUND)
34962306a36Sopenharmony_ci		return offset;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Try to place the new node after the parent's properties */
35262306a36Sopenharmony_ci	tag = fdt_next_tag(fdt, parentoffset, &nextoffset);
35362306a36Sopenharmony_ci	/* the fdt_subnode_offset_namelen() should ensure this never hits */
35462306a36Sopenharmony_ci	if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))
35562306a36Sopenharmony_ci		return -FDT_ERR_INTERNAL;
35662306a36Sopenharmony_ci	do {
35762306a36Sopenharmony_ci		offset = nextoffset;
35862306a36Sopenharmony_ci		tag = fdt_next_tag(fdt, offset, &nextoffset);
35962306a36Sopenharmony_ci	} while ((tag == FDT_PROP) || (tag == FDT_NOP));
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	nh = fdt_offset_ptr_w_(fdt, offset);
36262306a36Sopenharmony_ci	nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	err = fdt_splice_struct_(fdt, nh, 0, nodelen);
36562306a36Sopenharmony_ci	if (err)
36662306a36Sopenharmony_ci		return err;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
36962306a36Sopenharmony_ci	memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
37062306a36Sopenharmony_ci	memcpy(nh->name, name, namelen);
37162306a36Sopenharmony_ci	endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
37262306a36Sopenharmony_ci	*endtag = cpu_to_fdt32(FDT_END_NODE);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return offset;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ciint fdt_add_subnode(void *fdt, int parentoffset, const char *name)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ciint fdt_del_node(void *fdt, int nodeoffset)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int endoffset;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	endoffset = fdt_node_end_offset_(fdt, nodeoffset);
38962306a36Sopenharmony_ci	if (endoffset < 0)
39062306a36Sopenharmony_ci		return endoffset;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),
39362306a36Sopenharmony_ci				  endoffset - nodeoffset, 0);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic void fdt_packblocks_(const char *old, char *new,
39762306a36Sopenharmony_ci			    int mem_rsv_size,
39862306a36Sopenharmony_ci			    int struct_size,
39962306a36Sopenharmony_ci			    int strings_size)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	int mem_rsv_off, struct_off, strings_off;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
40462306a36Sopenharmony_ci	struct_off = mem_rsv_off + mem_rsv_size;
40562306a36Sopenharmony_ci	strings_off = struct_off + struct_size;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
40862306a36Sopenharmony_ci	fdt_set_off_mem_rsvmap(new, mem_rsv_off);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
41162306a36Sopenharmony_ci	fdt_set_off_dt_struct(new, struct_off);
41262306a36Sopenharmony_ci	fdt_set_size_dt_struct(new, struct_size);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);
41562306a36Sopenharmony_ci	fdt_set_off_dt_strings(new, strings_off);
41662306a36Sopenharmony_ci	fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ciint fdt_open_into(const void *fdt, void *buf, int bufsize)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	int err;
42262306a36Sopenharmony_ci	int mem_rsv_size, struct_size;
42362306a36Sopenharmony_ci	int newsize;
42462306a36Sopenharmony_ci	const char *fdtstart = fdt;
42562306a36Sopenharmony_ci	const char *fdtend = fdtstart + fdt_totalsize(fdt);
42662306a36Sopenharmony_ci	char *tmp;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	FDT_RO_PROBE(fdt);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
43162306a36Sopenharmony_ci		* sizeof(struct fdt_reserve_entry);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (can_assume(LATEST) || fdt_version(fdt) >= 17) {
43462306a36Sopenharmony_ci		struct_size = fdt_size_dt_struct(fdt);
43562306a36Sopenharmony_ci	} else if (fdt_version(fdt) == 16) {
43662306a36Sopenharmony_ci		struct_size = 0;
43762306a36Sopenharmony_ci		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
43862306a36Sopenharmony_ci			;
43962306a36Sopenharmony_ci		if (struct_size < 0)
44062306a36Sopenharmony_ci			return struct_size;
44162306a36Sopenharmony_ci	} else {
44262306a36Sopenharmony_ci		return -FDT_ERR_BADVERSION;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (can_assume(LIBFDT_ORDER) ||
44662306a36Sopenharmony_ci	    !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {
44762306a36Sopenharmony_ci		/* no further work necessary */
44862306a36Sopenharmony_ci		err = fdt_move(fdt, buf, bufsize);
44962306a36Sopenharmony_ci		if (err)
45062306a36Sopenharmony_ci			return err;
45162306a36Sopenharmony_ci		fdt_set_version(buf, 17);
45262306a36Sopenharmony_ci		fdt_set_size_dt_struct(buf, struct_size);
45362306a36Sopenharmony_ci		fdt_set_totalsize(buf, bufsize);
45462306a36Sopenharmony_ci		return 0;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Need to reorder */
45862306a36Sopenharmony_ci	newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
45962306a36Sopenharmony_ci		+ struct_size + fdt_size_dt_strings(fdt);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (bufsize < newsize)
46262306a36Sopenharmony_ci		return -FDT_ERR_NOSPACE;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* First attempt to build converted tree at beginning of buffer */
46562306a36Sopenharmony_ci	tmp = buf;
46662306a36Sopenharmony_ci	/* But if that overlaps with the old tree... */
46762306a36Sopenharmony_ci	if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
46862306a36Sopenharmony_ci		/* Try right after the old tree instead */
46962306a36Sopenharmony_ci		tmp = (char *)(uintptr_t)fdtend;
47062306a36Sopenharmony_ci		if ((tmp + newsize) > ((char *)buf + bufsize))
47162306a36Sopenharmony_ci			return -FDT_ERR_NOSPACE;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,
47562306a36Sopenharmony_ci			fdt_size_dt_strings(fdt));
47662306a36Sopenharmony_ci	memmove(buf, tmp, newsize);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	fdt_set_magic(buf, FDT_MAGIC);
47962306a36Sopenharmony_ci	fdt_set_totalsize(buf, bufsize);
48062306a36Sopenharmony_ci	fdt_set_version(buf, 17);
48162306a36Sopenharmony_ci	fdt_set_last_comp_version(buf, 16);
48262306a36Sopenharmony_ci	fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ciint fdt_pack(void *fdt)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	int mem_rsv_size;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	FDT_RW_PROBE(fdt);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
49462306a36Sopenharmony_ci		* sizeof(struct fdt_reserve_entry);
49562306a36Sopenharmony_ci	fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt),
49662306a36Sopenharmony_ci			fdt_size_dt_strings(fdt));
49762306a36Sopenharmony_ci	fdt_set_totalsize(fdt, fdt_data_size_(fdt));
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return 0;
50062306a36Sopenharmony_ci}
501