512 lines · 12.3 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)/* * libfdt - Flat Device Tree manipulation * Copyright (C) 2006 David Gibson, IBM Corporation. */#include "libfdt_env.h" #include <fdt.h>#include <libfdt.h> #include "libfdt_internal.h" static int fdt_blocks_misordered_(const void *fdt,				  int mem_rsv_size, int struct_size){	return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))		|| (fdt_off_dt_struct(fdt) <		    (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))		|| (fdt_off_dt_strings(fdt) <		    (fdt_off_dt_struct(fdt) + struct_size))		|| (fdt_totalsize(fdt) <		    (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));} static void fdt_downgrade_version(void *fdt){	if (!can_assume(LATEST) && fdt_version(fdt) > FDT_LAST_SUPPORTED_VERSION)		fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);} static int fdt_rw_probe_(void *fdt){	if (can_assume(VALID_DTB))		return 0;	FDT_RO_PROBE(fdt); 	if (!can_assume(LATEST) && fdt_version(fdt) < 17)		return -FDT_ERR_BADVERSION;	if (fdt_blocks_misordered_(fdt, sizeof(struct fdt_reserve_entry),				   fdt_size_dt_struct(fdt)))		return -FDT_ERR_BADLAYOUT; 	fdt_downgrade_version(fdt);	return 0;} #define FDT_RW_PROBE(fdt) \	{ \		int err_; \		if ((err_ = fdt_rw_probe_(fdt)) != 0) \			return err_; \	} static inline unsigned int fdt_data_size_(void *fdt){	return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);} static int fdt_splice_(void *fdt, void *splicepoint, int oldlen, int newlen){	char *p = splicepoint;	unsigned int dsize = fdt_data_size_(fdt);	size_t soff = p - (char *)fdt; 	if ((oldlen < 0) || (soff + oldlen < soff) || (soff + oldlen > dsize))		return -FDT_ERR_BADOFFSET;	if ((p < (char *)fdt) || (dsize + newlen < (unsigned)oldlen))		return -FDT_ERR_BADOFFSET;	if (dsize - oldlen + newlen > fdt_totalsize(fdt))		return -FDT_ERR_NOSPACE;	memmove(p + newlen, p + oldlen, ((char *)fdt + dsize) - (p + oldlen));	return 0;} static int fdt_splice_mem_rsv_(void *fdt, struct fdt_reserve_entry *p,			       int oldn, int newn){	int delta = (newn - oldn) * sizeof(*p);	int err;	err = fdt_splice_(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));	if (err)		return err;	fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);	return 0;} static int fdt_splice_struct_(void *fdt, void *p,			      int oldlen, int newlen){	int delta = newlen - oldlen;	int err; 	if ((err = fdt_splice_(fdt, p, oldlen, newlen)))		return err; 	fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);	fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);	return 0;} /* Must only be used to roll back in case of error */static void fdt_del_last_string_(void *fdt, const char *s){	int newlen = strlen(s) + 1; 	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) - newlen);} static int fdt_splice_string_(void *fdt, int newlen){	void *p = (char *)fdt		+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);	int err; 	if ((err = fdt_splice_(fdt, p, 0, newlen)))		return err; 	fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);	return 0;} /** * fdt_find_add_string_() - Find or allocate a string * * @fdt: pointer to the device tree to check/adjust * @s: string to find/add * @allocated: Set to 0 if the string was found, 1 if not found and so *	allocated. Ignored if can_assume(NO_ROLLBACK) * @return offset of string in the string table (whether found or added) */static int fdt_find_add_string_(void *fdt, const char *s, int slen,				int *allocated){	char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);	const char *p;	char *new;	int err; 	if (!can_assume(NO_ROLLBACK))		*allocated = 0; 	p = fdt_find_string_len_(strtab, fdt_size_dt_strings(fdt), s, slen);	if (p)		/* found it */		return (p - strtab); 	new = strtab + fdt_size_dt_strings(fdt);	err = fdt_splice_string_(fdt, slen + 1);	if (err)		return err; 	if (!can_assume(NO_ROLLBACK))		*allocated = 1; 	memcpy(new, s, slen);	new[slen] = '\0'; 	return (new - strtab);} int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size){	struct fdt_reserve_entry *re;	int err; 	FDT_RW_PROBE(fdt); 	re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt));	err = fdt_splice_mem_rsv_(fdt, re, 0, 1);	if (err)		return err; 	re->address = cpu_to_fdt64(address);	re->size = cpu_to_fdt64(size);	return 0;} int fdt_del_mem_rsv(void *fdt, int n){	struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); 	FDT_RW_PROBE(fdt); 	if (n >= fdt_num_mem_rsv(fdt))		return -FDT_ERR_NOTFOUND; 	return fdt_splice_mem_rsv_(fdt, re, 1, 0);} static int fdt_resize_property_(void *fdt, int nodeoffset,				const char *name, int namelen,				int len, struct fdt_property **prop){	int oldlen;	int err; 	*prop = fdt_get_property_namelen_w(fdt, nodeoffset, name, namelen,					   &oldlen);	if (!*prop)		return oldlen; 	if ((err = fdt_splice_struct_(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),				      FDT_TAGALIGN(len))))		return err; 	(*prop)->len = cpu_to_fdt32(len);	return 0;} static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,			     int namelen, int len, struct fdt_property **prop){	int proplen;	int nextoffset;	int namestroff;	int err;	int allocated; 	if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)		return nextoffset; 	namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated);	if (namestroff < 0)		return namestroff; 	*prop = fdt_offset_ptr_w_(fdt, nextoffset);	proplen = sizeof(**prop) + FDT_TAGALIGN(len); 	err = fdt_splice_struct_(fdt, *prop, 0, proplen);	if (err) {		/* Delete the string if we failed to add it */		if (!can_assume(NO_ROLLBACK) && allocated)			fdt_del_last_string_(fdt, name);		return err;	} 	(*prop)->tag = cpu_to_fdt32(FDT_PROP);	(*prop)->nameoff = cpu_to_fdt32(namestroff);	(*prop)->len = cpu_to_fdt32(len);	return 0;} int fdt_set_name(void *fdt, int nodeoffset, const char *name){	char *namep;	int oldlen, newlen;	int err; 	FDT_RW_PROBE(fdt); 	namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);	if (!namep)		return oldlen; 	newlen = strlen(name); 	err = fdt_splice_struct_(fdt, namep, FDT_TAGALIGN(oldlen+1),				 FDT_TAGALIGN(newlen+1));	if (err)		return err; 	memcpy(namep, name, newlen+1);	return 0;} int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,				    int namelen, int len, void **prop_data){	struct fdt_property *prop;	int err; 	FDT_RW_PROBE(fdt); 	err = fdt_resize_property_(fdt, nodeoffset, name, namelen, len, &prop);	if (err == -FDT_ERR_NOTFOUND)		err = fdt_add_property_(fdt, nodeoffset, name, namelen, len,					&prop);	if (err)		return err; 	*prop_data = prop->data;	return 0;} int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,			int namelen, const void *val, int len){	void *prop_data;	int err; 	err = fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, namelen,					      len, &prop_data);	if (err)		return err; 	if (len)		memcpy(prop_data, val, len);	return 0;} int fdt_appendprop(void *fdt, int nodeoffset, const char *name,		   const void *val, int len){	struct fdt_property *prop;	int err, oldlen, newlen; 	FDT_RW_PROBE(fdt); 	prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);	if (prop) {		newlen = len + oldlen;		err = fdt_splice_struct_(fdt, prop->data,					 FDT_TAGALIGN(oldlen),					 FDT_TAGALIGN(newlen));		if (err)			return err;		prop->len = cpu_to_fdt32(newlen);		memcpy(prop->data + oldlen, val, len);	} else {		err = fdt_add_property_(fdt, nodeoffset, name, strlen(name),					len, &prop);		if (err)			return err;		memcpy(prop->data, val, len);	}	return 0;} int fdt_delprop(void *fdt, int nodeoffset, const char *name){	struct fdt_property *prop;	int len, proplen; 	FDT_RW_PROBE(fdt); 	prop = fdt_get_property_w(fdt, nodeoffset, name, &len);	if (!prop)		return len; 	proplen = sizeof(*prop) + FDT_TAGALIGN(len);	return fdt_splice_struct_(fdt, prop, proplen, 0);} int fdt_add_subnode_namelen(void *fdt, int parentoffset,			    const char *name, int namelen){	struct fdt_node_header *nh;	int offset, nextoffset;	int nodelen;	int err;	uint32_t tag;	fdt32_t *endtag; 	FDT_RW_PROBE(fdt); 	offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);	if (offset >= 0)		return -FDT_ERR_EXISTS;	else if (offset != -FDT_ERR_NOTFOUND)		return offset; 	/* Try to place the new node after the parent's properties */	tag = fdt_next_tag(fdt, parentoffset, &nextoffset);	/* the fdt_subnode_offset_namelen() should ensure this never hits */	if (!can_assume(LIBFDT_FLAWLESS) && (tag != FDT_BEGIN_NODE))		return -FDT_ERR_INTERNAL;	do {		offset = nextoffset;		tag = fdt_next_tag(fdt, offset, &nextoffset);	} while ((tag == FDT_PROP) || (tag == FDT_NOP)); 	nh = fdt_offset_ptr_w_(fdt, offset);	nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; 	err = fdt_splice_struct_(fdt, nh, 0, nodelen);	if (err)		return err; 	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);	memset(nh->name, 0, FDT_TAGALIGN(namelen+1));	memcpy(nh->name, name, namelen);	endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);	*endtag = cpu_to_fdt32(FDT_END_NODE); 	return offset;} int fdt_add_subnode(void *fdt, int parentoffset, const char *name){	return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));} int fdt_del_node(void *fdt, int nodeoffset){	int endoffset; 	FDT_RW_PROBE(fdt); 	endoffset = fdt_node_end_offset_(fdt, nodeoffset);	if (endoffset < 0)		return endoffset; 	return fdt_splice_struct_(fdt, fdt_offset_ptr_w_(fdt, nodeoffset),				  endoffset - nodeoffset, 0);} static void fdt_packblocks_(const char *old, char *new,			    int mem_rsv_size,			    int struct_size,			    int strings_size){	int mem_rsv_off, struct_off, strings_off; 	mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);	struct_off = mem_rsv_off + mem_rsv_size;	strings_off = struct_off + struct_size; 	memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);	fdt_set_off_mem_rsvmap(new, mem_rsv_off); 	memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);	fdt_set_off_dt_struct(new, struct_off);	fdt_set_size_dt_struct(new, struct_size); 	memmove(new + strings_off, old + fdt_off_dt_strings(old), strings_size);	fdt_set_off_dt_strings(new, strings_off);	fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));} int fdt_open_into(const void *fdt, void *buf, int bufsize){	int err;	int mem_rsv_size, struct_size;	int newsize;	const char *fdtstart = fdt;	const char *fdtend = fdtstart + fdt_totalsize(fdt);	char *tmp; 	FDT_RO_PROBE(fdt); 	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)		* sizeof(struct fdt_reserve_entry); 	if (can_assume(LATEST) || fdt_version(fdt) >= 17) {		struct_size = fdt_size_dt_struct(fdt);	} else if (fdt_version(fdt) == 16) {		struct_size = 0;		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)			;		if (struct_size < 0)			return struct_size;	} else {		return -FDT_ERR_BADVERSION;	} 	if (can_assume(LIBFDT_ORDER) ||	    !fdt_blocks_misordered_(fdt, mem_rsv_size, struct_size)) {		/* no further work necessary */		err = fdt_move(fdt, buf, bufsize);		if (err)			return err;		fdt_set_version(buf, 17);		fdt_set_size_dt_struct(buf, struct_size);		fdt_set_totalsize(buf, bufsize);		return 0;	} 	/* Need to reorder */	newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size		+ struct_size + fdt_size_dt_strings(fdt); 	if (bufsize < newsize)		return -FDT_ERR_NOSPACE; 	/* First attempt to build converted tree at beginning of buffer */	tmp = buf;	/* But if that overlaps with the old tree... */	if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {		/* Try right after the old tree instead */		tmp = (char *)(uintptr_t)fdtend;		if ((tmp + newsize) > ((char *)buf + bufsize))			return -FDT_ERR_NOSPACE;	} 	fdt_packblocks_(fdt, tmp, mem_rsv_size, struct_size,			fdt_size_dt_strings(fdt));	memmove(buf, tmp, newsize); 	fdt_set_magic(buf, FDT_MAGIC);	fdt_set_totalsize(buf, bufsize);	fdt_set_version(buf, 17);	fdt_set_last_comp_version(buf, 16);	fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); 	return 0;} int fdt_pack(void *fdt){

Lines 1–500 of 512.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t