18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * libfdt - Flat Device Tree manipulation
48c2ecf20Sopenharmony_ci * Copyright (C) 2006 David Gibson, IBM Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include "libfdt_env.h"
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <fdt.h>
98c2ecf20Sopenharmony_ci#include <libfdt.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "libfdt_internal.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic int fdt_sw_probe_(void *fdt)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	if (!can_assume(VALID_INPUT)) {
168c2ecf20Sopenharmony_ci		if (fdt_magic(fdt) == FDT_MAGIC)
178c2ecf20Sopenharmony_ci			return -FDT_ERR_BADSTATE;
188c2ecf20Sopenharmony_ci		else if (fdt_magic(fdt) != FDT_SW_MAGIC)
198c2ecf20Sopenharmony_ci			return -FDT_ERR_BADMAGIC;
208c2ecf20Sopenharmony_ci	}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	return 0;
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define FDT_SW_PROBE(fdt) \
268c2ecf20Sopenharmony_ci	{ \
278c2ecf20Sopenharmony_ci		int err; \
288c2ecf20Sopenharmony_ci		if ((err = fdt_sw_probe_(fdt)) != 0) \
298c2ecf20Sopenharmony_ci			return err; \
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* 'memrsv' state:	Initial state after fdt_create()
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * Allowed functions:
358c2ecf20Sopenharmony_ci *	fdt_add_reservemap_entry()
368c2ecf20Sopenharmony_ci *	fdt_finish_reservemap()		[moves to 'struct' state]
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic int fdt_sw_probe_memrsv_(void *fdt)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int err = fdt_sw_probe_(fdt);
418c2ecf20Sopenharmony_ci	if (err)
428c2ecf20Sopenharmony_ci		return err;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (!can_assume(VALID_INPUT) && fdt_off_dt_strings(fdt) != 0)
458c2ecf20Sopenharmony_ci		return -FDT_ERR_BADSTATE;
468c2ecf20Sopenharmony_ci	return 0;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define FDT_SW_PROBE_MEMRSV(fdt) \
508c2ecf20Sopenharmony_ci	{ \
518c2ecf20Sopenharmony_ci		int err; \
528c2ecf20Sopenharmony_ci		if ((err = fdt_sw_probe_memrsv_(fdt)) != 0) \
538c2ecf20Sopenharmony_ci			return err; \
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* 'struct' state:	Enter this state after fdt_finish_reservemap()
578c2ecf20Sopenharmony_ci *
588c2ecf20Sopenharmony_ci * Allowed functions:
598c2ecf20Sopenharmony_ci *	fdt_begin_node()
608c2ecf20Sopenharmony_ci *	fdt_end_node()
618c2ecf20Sopenharmony_ci *	fdt_property*()
628c2ecf20Sopenharmony_ci *	fdt_finish()			[moves to 'complete' state]
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic int fdt_sw_probe_struct_(void *fdt)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	int err = fdt_sw_probe_(fdt);
678c2ecf20Sopenharmony_ci	if (err)
688c2ecf20Sopenharmony_ci		return err;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (!can_assume(VALID_INPUT) &&
718c2ecf20Sopenharmony_ci	    fdt_off_dt_strings(fdt) != fdt_totalsize(fdt))
728c2ecf20Sopenharmony_ci		return -FDT_ERR_BADSTATE;
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define FDT_SW_PROBE_STRUCT(fdt) \
778c2ecf20Sopenharmony_ci	{ \
788c2ecf20Sopenharmony_ci		int err; \
798c2ecf20Sopenharmony_ci		if ((err = fdt_sw_probe_struct_(fdt)) != 0) \
808c2ecf20Sopenharmony_ci			return err; \
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic inline uint32_t sw_flags(void *fdt)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	/* assert: (fdt_magic(fdt) == FDT_SW_MAGIC) */
868c2ecf20Sopenharmony_ci	return fdt_last_comp_version(fdt);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/* 'complete' state:	Enter this state after fdt_finish()
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * Allowed functions: none
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void *fdt_grab_space_(void *fdt, size_t len)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	unsigned int offset = fdt_size_dt_struct(fdt);
978c2ecf20Sopenharmony_ci	unsigned int spaceleft;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
1008c2ecf20Sopenharmony_ci		- fdt_size_dt_strings(fdt);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if ((offset + len < offset) || (offset + len > spaceleft))
1038c2ecf20Sopenharmony_ci		return NULL;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	fdt_set_size_dt_struct(fdt, offset + len);
1068c2ecf20Sopenharmony_ci	return fdt_offset_ptr_w_(fdt, offset);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciint fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	const int hdrsize = FDT_ALIGN(sizeof(struct fdt_header),
1128c2ecf20Sopenharmony_ci				      sizeof(struct fdt_reserve_entry));
1138c2ecf20Sopenharmony_ci	void *fdt = buf;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (bufsize < hdrsize)
1168c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (flags & ~FDT_CREATE_FLAGS_ALL)
1198c2ecf20Sopenharmony_ci		return -FDT_ERR_BADFLAGS;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	memset(buf, 0, bufsize);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/*
1248c2ecf20Sopenharmony_ci	 * magic and last_comp_version keep intermediate state during the fdt
1258c2ecf20Sopenharmony_ci	 * creation process, which is replaced with the proper FDT format by
1268c2ecf20Sopenharmony_ci	 * fdt_finish().
1278c2ecf20Sopenharmony_ci	 *
1288c2ecf20Sopenharmony_ci	 * flags should be accessed with sw_flags().
1298c2ecf20Sopenharmony_ci	 */
1308c2ecf20Sopenharmony_ci	fdt_set_magic(fdt, FDT_SW_MAGIC);
1318c2ecf20Sopenharmony_ci	fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
1328c2ecf20Sopenharmony_ci	fdt_set_last_comp_version(fdt, flags);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	fdt_set_totalsize(fdt,  bufsize);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	fdt_set_off_mem_rsvmap(fdt, hdrsize);
1378c2ecf20Sopenharmony_ci	fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
1388c2ecf20Sopenharmony_ci	fdt_set_off_dt_strings(fdt, 0);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return 0;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciint fdt_create(void *buf, int bufsize)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	return fdt_create_with_flags(buf, bufsize, 0);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciint fdt_resize(void *fdt, void *buf, int bufsize)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	size_t headsize, tailsize;
1518c2ecf20Sopenharmony_ci	char *oldtail, *newtail;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	FDT_SW_PROBE(fdt);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (bufsize < 0)
1568c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	headsize = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
1598c2ecf20Sopenharmony_ci	tailsize = fdt_size_dt_strings(fdt);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!can_assume(VALID_DTB) &&
1628c2ecf20Sopenharmony_ci	    headsize + tailsize > fdt_totalsize(fdt))
1638c2ecf20Sopenharmony_ci		return -FDT_ERR_INTERNAL;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if ((headsize + tailsize) > (unsigned)bufsize)
1668c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
1698c2ecf20Sopenharmony_ci	newtail = (char *)buf + bufsize - tailsize;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* Two cases to avoid clobbering data if the old and new
1728c2ecf20Sopenharmony_ci	 * buffers partially overlap */
1738c2ecf20Sopenharmony_ci	if (buf <= fdt) {
1748c2ecf20Sopenharmony_ci		memmove(buf, fdt, headsize);
1758c2ecf20Sopenharmony_ci		memmove(newtail, oldtail, tailsize);
1768c2ecf20Sopenharmony_ci	} else {
1778c2ecf20Sopenharmony_ci		memmove(newtail, oldtail, tailsize);
1788c2ecf20Sopenharmony_ci		memmove(buf, fdt, headsize);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	fdt_set_totalsize(buf, bufsize);
1828c2ecf20Sopenharmony_ci	if (fdt_off_dt_strings(buf))
1838c2ecf20Sopenharmony_ci		fdt_set_off_dt_strings(buf, bufsize);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ciint fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct fdt_reserve_entry *re;
1918c2ecf20Sopenharmony_ci	int offset;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	FDT_SW_PROBE_MEMRSV(fdt);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	offset = fdt_off_dt_struct(fdt);
1968c2ecf20Sopenharmony_ci	if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
1978c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	re = (struct fdt_reserve_entry *)((char *)fdt + offset);
2008c2ecf20Sopenharmony_ci	re->address = cpu_to_fdt64(addr);
2018c2ecf20Sopenharmony_ci	re->size = cpu_to_fdt64(size);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return 0;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ciint fdt_finish_reservemap(void *fdt)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	int err = fdt_add_reservemap_entry(fdt, 0, 0);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (err)
2138c2ecf20Sopenharmony_ci		return err;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	fdt_set_off_dt_strings(fdt, fdt_totalsize(fdt));
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciint fdt_begin_node(void *fdt, const char *name)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct fdt_node_header *nh;
2228c2ecf20Sopenharmony_ci	int namelen;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	FDT_SW_PROBE_STRUCT(fdt);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	namelen = strlen(name) + 1;
2278c2ecf20Sopenharmony_ci	nh = fdt_grab_space_(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
2288c2ecf20Sopenharmony_ci	if (! nh)
2298c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
2328c2ecf20Sopenharmony_ci	memcpy(nh->name, name, namelen);
2338c2ecf20Sopenharmony_ci	return 0;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ciint fdt_end_node(void *fdt)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	fdt32_t *en;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	FDT_SW_PROBE_STRUCT(fdt);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	en = fdt_grab_space_(fdt, FDT_TAGSIZE);
2438c2ecf20Sopenharmony_ci	if (! en)
2448c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	*en = cpu_to_fdt32(FDT_END_NODE);
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int fdt_add_string_(void *fdt, const char *s)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	char *strtab = (char *)fdt + fdt_totalsize(fdt);
2538c2ecf20Sopenharmony_ci	unsigned int strtabsize = fdt_size_dt_strings(fdt);
2548c2ecf20Sopenharmony_ci	unsigned int len = strlen(s) + 1;
2558c2ecf20Sopenharmony_ci	unsigned int struct_top, offset;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	offset = strtabsize + len;
2588c2ecf20Sopenharmony_ci	struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
2598c2ecf20Sopenharmony_ci	if (fdt_totalsize(fdt) - offset < struct_top)
2608c2ecf20Sopenharmony_ci		return 0; /* no more room :( */
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	memcpy(strtab - offset, s, len);
2638c2ecf20Sopenharmony_ci	fdt_set_size_dt_strings(fdt, strtabsize + len);
2648c2ecf20Sopenharmony_ci	return -offset;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/* Must only be used to roll back in case of error */
2688c2ecf20Sopenharmony_cistatic void fdt_del_last_string_(void *fdt, const char *s)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	int strtabsize = fdt_size_dt_strings(fdt);
2718c2ecf20Sopenharmony_ci	int len = strlen(s) + 1;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	fdt_set_size_dt_strings(fdt, strtabsize - len);
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int fdt_find_add_string_(void *fdt, const char *s, int *allocated)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	char *strtab = (char *)fdt + fdt_totalsize(fdt);
2798c2ecf20Sopenharmony_ci	int strtabsize = fdt_size_dt_strings(fdt);
2808c2ecf20Sopenharmony_ci	const char *p;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	*allocated = 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	p = fdt_find_string_(strtab - strtabsize, strtabsize, s);
2858c2ecf20Sopenharmony_ci	if (p)
2868c2ecf20Sopenharmony_ci		return p - strtab;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	*allocated = 1;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	return fdt_add_string_(fdt, s);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ciint fdt_property_placeholder(void *fdt, const char *name, int len, void **valp)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct fdt_property *prop;
2968c2ecf20Sopenharmony_ci	int nameoff;
2978c2ecf20Sopenharmony_ci	int allocated;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	FDT_SW_PROBE_STRUCT(fdt);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* String de-duplication can be slow, _NO_NAME_DEDUP skips it */
3028c2ecf20Sopenharmony_ci	if (sw_flags(fdt) & FDT_CREATE_FLAG_NO_NAME_DEDUP) {
3038c2ecf20Sopenharmony_ci		allocated = 1;
3048c2ecf20Sopenharmony_ci		nameoff = fdt_add_string_(fdt, name);
3058c2ecf20Sopenharmony_ci	} else {
3068c2ecf20Sopenharmony_ci		nameoff = fdt_find_add_string_(fdt, name, &allocated);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci	if (nameoff == 0)
3098c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	prop = fdt_grab_space_(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
3128c2ecf20Sopenharmony_ci	if (! prop) {
3138c2ecf20Sopenharmony_ci		if (allocated)
3148c2ecf20Sopenharmony_ci			fdt_del_last_string_(fdt, name);
3158c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	prop->tag = cpu_to_fdt32(FDT_PROP);
3198c2ecf20Sopenharmony_ci	prop->nameoff = cpu_to_fdt32(nameoff);
3208c2ecf20Sopenharmony_ci	prop->len = cpu_to_fdt32(len);
3218c2ecf20Sopenharmony_ci	*valp = prop->data;
3228c2ecf20Sopenharmony_ci	return 0;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ciint fdt_property(void *fdt, const char *name, const void *val, int len)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	void *ptr;
3288c2ecf20Sopenharmony_ci	int ret;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = fdt_property_placeholder(fdt, name, len, &ptr);
3318c2ecf20Sopenharmony_ci	if (ret)
3328c2ecf20Sopenharmony_ci		return ret;
3338c2ecf20Sopenharmony_ci	memcpy(ptr, val, len);
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ciint fdt_finish(void *fdt)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	char *p = (char *)fdt;
3408c2ecf20Sopenharmony_ci	fdt32_t *end;
3418c2ecf20Sopenharmony_ci	int oldstroffset, newstroffset;
3428c2ecf20Sopenharmony_ci	uint32_t tag;
3438c2ecf20Sopenharmony_ci	int offset, nextoffset;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	FDT_SW_PROBE_STRUCT(fdt);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Add terminator */
3488c2ecf20Sopenharmony_ci	end = fdt_grab_space_(fdt, sizeof(*end));
3498c2ecf20Sopenharmony_ci	if (! end)
3508c2ecf20Sopenharmony_ci		return -FDT_ERR_NOSPACE;
3518c2ecf20Sopenharmony_ci	*end = cpu_to_fdt32(FDT_END);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* Relocate the string table */
3548c2ecf20Sopenharmony_ci	oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
3558c2ecf20Sopenharmony_ci	newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
3568c2ecf20Sopenharmony_ci	memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
3578c2ecf20Sopenharmony_ci	fdt_set_off_dt_strings(fdt, newstroffset);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* Walk the structure, correcting string offsets */
3608c2ecf20Sopenharmony_ci	offset = 0;
3618c2ecf20Sopenharmony_ci	while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
3628c2ecf20Sopenharmony_ci		if (tag == FDT_PROP) {
3638c2ecf20Sopenharmony_ci			struct fdt_property *prop =
3648c2ecf20Sopenharmony_ci				fdt_offset_ptr_w_(fdt, offset);
3658c2ecf20Sopenharmony_ci			int nameoff;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci			nameoff = fdt32_to_cpu(prop->nameoff);
3688c2ecf20Sopenharmony_ci			nameoff += fdt_size_dt_strings(fdt);
3698c2ecf20Sopenharmony_ci			prop->nameoff = cpu_to_fdt32(nameoff);
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci		offset = nextoffset;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci	if (nextoffset < 0)
3748c2ecf20Sopenharmony_ci		return nextoffset;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Finally, adjust the header */
3778c2ecf20Sopenharmony_ci	fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* And fix up fields that were keeping intermediate state. */
3808c2ecf20Sopenharmony_ci	fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
3818c2ecf20Sopenharmony_ci	fdt_set_magic(fdt, FDT_MAGIC);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return 0;
3848c2ecf20Sopenharmony_ci}
385