mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 18:35:42 +01:00 
			
		
		
		
	scripts/dtc: Update to upstream version v1.4.3
Using the update-dtc-source.sh script from Linux v4.14-rc1 import the portions of dtc that we require. We bring in update-dtc-source.sh and scripts/dtc/Makefile from Linux v4.14-rc1. Rework DTC_FLAGS handling to not require a test. Signed-off-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
		| @@ -172,11 +172,6 @@ ld-version = $(shell $(LD) --version | $(srctree)/scripts/ld-version.sh) | |||||||
| # Usage:  $(call ld-ifversion, -ge, 22252, y) | # Usage:  $(call ld-ifversion, -ge, 22252, y) | ||||||
| ld-ifversion = $(shell [ $(ld-version) $(1) $(2) ] && echo $(3) || echo $(4)) | ld-ifversion = $(shell [ $(ld-version) $(1) $(2) ] && echo $(3) || echo $(4)) | ||||||
|  |  | ||||||
| # dtc-option |  | ||||||
| # Usage:  DTC_FLAGS += $(call dtc-option,-Wno-unit_address_vs_reg) |  | ||||||
| dtc-option = $(call try-run,\ |  | ||||||
| 	echo '/dts-v1/; / {};' | $(DTC) $(1),$(1),$(2)) |  | ||||||
|  |  | ||||||
| ###### | ###### | ||||||
|  |  | ||||||
| ### | ### | ||||||
|   | |||||||
| @@ -58,8 +58,8 @@ endif | |||||||
|  |  | ||||||
| KBUILD_CFLAGS += $(warning) | KBUILD_CFLAGS += $(warning) | ||||||
|  |  | ||||||
| dtc-warning-2 += $(call dtc-option,-Wnode_name_chars_strict) | dtc-warning-2 += -Wnode_name_chars_strict | ||||||
| dtc-warning-2 += $(call dtc-option,-Wproperty_name_chars_strict) | dtc-warning-2 += -Wproperty_name_chars_strict | ||||||
|  |  | ||||||
| dtc-warning := $(dtc-warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) | dtc-warning := $(dtc-warning-$(findstring 1, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) | ||||||
| dtc-warning += $(dtc-warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) | dtc-warning += $(dtc-warning-$(findstring 2, $(KBUILD_ENABLE_EXTRA_GCC_CHECKS))) | ||||||
| @@ -70,11 +70,11 @@ DTC_FLAGS += $(dtc-warning) | |||||||
| else | else | ||||||
|  |  | ||||||
| # Disable noisy checks by default | # Disable noisy checks by default | ||||||
| DTC_FLAGS += $(call dtc-option,-Wno-unit_address_vs_reg) | DTC_FLAGS += -Wno-unit_address_vs_reg | ||||||
| DTC_FLAGS += $(call dtc-option,-Wno-simple_bus_reg) | DTC_FLAGS += -Wno-simple_bus_reg | ||||||
| DTC_FLAGS += $(call dtc-option,-Wno-unit_address_format) | DTC_FLAGS += -Wno-unit_address_format | ||||||
| DTC_FLAGS += $(call dtc-option,-Wno-pci_bridge) | DTC_FLAGS += -Wno-pci_bridge | ||||||
| DTC_FLAGS += $(call dtc-option,-Wno-pci_device_bus_num) | DTC_FLAGS += -Wno-pci_device_bus_num | ||||||
| DTC_FLAGS += $(call dtc-option,-Wno-pci_device_reg) | DTC_FLAGS += -Wno-pci_device_reg | ||||||
|  |  | ||||||
| endif | endif | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								scripts/dtc/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								scripts/dtc/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | # scripts/dtc makefile | ||||||
|  |  | ||||||
|  | hostprogs-y	:= dtc | ||||||
|  | always		:= $(hostprogs-y) | ||||||
|  |  | ||||||
|  | dtc-objs	:= dtc.o flattree.o fstree.o data.o livetree.o treesource.o \ | ||||||
|  | 		   srcpos.o checks.o util.o | ||||||
|  | dtc-objs	+= dtc-lexer.lex.o dtc-parser.tab.o | ||||||
|  |  | ||||||
|  | # Source files need to get at the userspace version of libfdt_env.h to compile | ||||||
|  |  | ||||||
|  | HOSTCFLAGS_DTC := -I$(src) -I$(src)/libfdt | ||||||
|  |  | ||||||
|  | HOSTCFLAGS_checks.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_data.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_dtc.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_flattree.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_fstree.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_livetree.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_srcpos.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_treesource.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_util.o := $(HOSTCFLAGS_DTC) | ||||||
|  |  | ||||||
|  | HOSTCFLAGS_dtc-lexer.lex.o := $(HOSTCFLAGS_DTC) | ||||||
|  | HOSTCFLAGS_dtc-parser.tab.o := $(HOSTCFLAGS_DTC) | ||||||
|  |  | ||||||
|  | # dependencies on generated files need to be listed explicitly | ||||||
|  | $(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h | ||||||
|  |  | ||||||
|  | # generated files need to be cleaned explicitly | ||||||
|  | clean-files	:= dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h | ||||||
							
								
								
									
										18
									
								
								scripts/dtc/Makefile.dtc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								scripts/dtc/Makefile.dtc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | # Makefile.dtc | ||||||
|  | # | ||||||
|  | # This is not a complete Makefile of itself.  Instead, it is designed to | ||||||
|  | # be easily embeddable into other systems of Makefiles. | ||||||
|  | # | ||||||
|  | DTC_SRCS = \ | ||||||
|  | 	checks.c \ | ||||||
|  | 	data.c \ | ||||||
|  | 	dtc.c \ | ||||||
|  | 	flattree.c \ | ||||||
|  | 	fstree.c \ | ||||||
|  | 	livetree.c \ | ||||||
|  | 	srcpos.c \ | ||||||
|  | 	treesource.c \ | ||||||
|  | 	util.c | ||||||
|  |  | ||||||
|  | DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c | ||||||
|  | DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o) | ||||||
							
								
								
									
										849
									
								
								scripts/dtc/checks.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										849
									
								
								scripts/dtc/checks.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,849 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2007. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  |  | ||||||
|  | #ifdef TRACE_CHECKS | ||||||
|  | #define TRACE(c, ...) \ | ||||||
|  | 	do { \ | ||||||
|  | 		fprintf(stderr, "=== %s: ", (c)->name); \ | ||||||
|  | 		fprintf(stderr, __VA_ARGS__); \ | ||||||
|  | 		fprintf(stderr, "\n"); \ | ||||||
|  | 	} while (0) | ||||||
|  | #else | ||||||
|  | #define TRACE(c, fmt, ...)	do { } while (0) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | enum checkstatus { | ||||||
|  | 	UNCHECKED = 0, | ||||||
|  | 	PREREQ, | ||||||
|  | 	PASSED, | ||||||
|  | 	FAILED, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct check; | ||||||
|  |  | ||||||
|  | typedef void (*check_fn)(struct check *c, struct dt_info *dti, struct node *node); | ||||||
|  |  | ||||||
|  | struct check { | ||||||
|  | 	const char *name; | ||||||
|  | 	check_fn fn; | ||||||
|  | 	void *data; | ||||||
|  | 	bool warn, error; | ||||||
|  | 	enum checkstatus status; | ||||||
|  | 	bool inprogress; | ||||||
|  | 	int num_prereqs; | ||||||
|  | 	struct check **prereq; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define CHECK_ENTRY(_nm, _fn, _d, _w, _e, ...)	       \ | ||||||
|  | 	static struct check *_nm##_prereqs[] = { __VA_ARGS__ }; \ | ||||||
|  | 	static struct check _nm = { \ | ||||||
|  | 		.name = #_nm, \ | ||||||
|  | 		.fn = (_fn), \ | ||||||
|  | 		.data = (_d), \ | ||||||
|  | 		.warn = (_w), \ | ||||||
|  | 		.error = (_e), \ | ||||||
|  | 		.status = UNCHECKED, \ | ||||||
|  | 		.num_prereqs = ARRAY_SIZE(_nm##_prereqs), \ | ||||||
|  | 		.prereq = _nm##_prereqs, \ | ||||||
|  | 	}; | ||||||
|  | #define WARNING(_nm, _fn, _d, ...) \ | ||||||
|  | 	CHECK_ENTRY(_nm, _fn, _d, true, false, __VA_ARGS__) | ||||||
|  | #define ERROR(_nm, _fn, _d, ...) \ | ||||||
|  | 	CHECK_ENTRY(_nm, _fn, _d, false, true, __VA_ARGS__) | ||||||
|  | #define CHECK(_nm, _fn, _d, ...) \ | ||||||
|  | 	CHECK_ENTRY(_nm, _fn, _d, false, false, __VA_ARGS__) | ||||||
|  |  | ||||||
|  | #ifdef __GNUC__ | ||||||
|  | static inline void check_msg(struct check *c, struct dt_info *dti, | ||||||
|  | 			     const char *fmt, ...) __attribute__((format (printf, 3, 4))); | ||||||
|  | #endif | ||||||
|  | static inline void check_msg(struct check *c, struct dt_info *dti, | ||||||
|  | 			     const char *fmt, ...) | ||||||
|  | { | ||||||
|  | 	va_list ap; | ||||||
|  | 	va_start(ap, fmt); | ||||||
|  |  | ||||||
|  | 	if ((c->warn && (quiet < 1)) | ||||||
|  | 	    || (c->error && (quiet < 2))) { | ||||||
|  | 		fprintf(stderr, "%s: %s (%s): ", | ||||||
|  | 			strcmp(dti->outname, "-") ? dti->outname : "<stdout>", | ||||||
|  | 			(c->error) ? "ERROR" : "Warning", c->name); | ||||||
|  | 		vfprintf(stderr, fmt, ap); | ||||||
|  | 		fprintf(stderr, "\n"); | ||||||
|  | 	} | ||||||
|  | 	va_end(ap); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define FAIL(c, dti, ...)						\ | ||||||
|  | 	do {								\ | ||||||
|  | 		TRACE((c), "\t\tFAILED at %s:%d", __FILE__, __LINE__);	\ | ||||||
|  | 		(c)->status = FAILED;					\ | ||||||
|  | 		check_msg((c), dti, __VA_ARGS__);			\ | ||||||
|  | 	} while (0) | ||||||
|  |  | ||||||
|  | static void check_nodes_props(struct check *c, struct dt_info *dti, struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *child; | ||||||
|  |  | ||||||
|  | 	TRACE(c, "%s", node->fullpath); | ||||||
|  | 	if (c->fn) | ||||||
|  | 		c->fn(c, dti, node); | ||||||
|  |  | ||||||
|  | 	for_each_child(node, child) | ||||||
|  | 		check_nodes_props(c, dti, child); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool run_check(struct check *c, struct dt_info *dti) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	bool error = false; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	assert(!c->inprogress); | ||||||
|  |  | ||||||
|  | 	if (c->status != UNCHECKED) | ||||||
|  | 		goto out; | ||||||
|  |  | ||||||
|  | 	c->inprogress = true; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < c->num_prereqs; i++) { | ||||||
|  | 		struct check *prq = c->prereq[i]; | ||||||
|  | 		error = error || run_check(prq, dti); | ||||||
|  | 		if (prq->status != PASSED) { | ||||||
|  | 			c->status = PREREQ; | ||||||
|  | 			check_msg(c, dti, "Failed prerequisite '%s'", | ||||||
|  | 				  c->prereq[i]->name); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (c->status != UNCHECKED) | ||||||
|  | 		goto out; | ||||||
|  |  | ||||||
|  | 	check_nodes_props(c, dti, dt); | ||||||
|  |  | ||||||
|  | 	if (c->status == UNCHECKED) | ||||||
|  | 		c->status = PASSED; | ||||||
|  |  | ||||||
|  | 	TRACE(c, "\tCompleted, status %d", c->status); | ||||||
|  |  | ||||||
|  | out: | ||||||
|  | 	c->inprogress = false; | ||||||
|  | 	if ((c->status != PASSED) && (c->error)) | ||||||
|  | 		error = true; | ||||||
|  | 	return error; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Utility check functions | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* A check which always fails, for testing purposes only */ | ||||||
|  | static inline void check_always_fail(struct check *c, struct dt_info *dti, | ||||||
|  | 				     struct node *node) | ||||||
|  | { | ||||||
|  | 	FAIL(c, dti, "always_fail check"); | ||||||
|  | } | ||||||
|  | CHECK(always_fail, check_always_fail, NULL); | ||||||
|  |  | ||||||
|  | static void check_is_string(struct check *c, struct dt_info *dti, | ||||||
|  | 			    struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	char *propname = c->data; | ||||||
|  |  | ||||||
|  | 	prop = get_property(node, propname); | ||||||
|  | 	if (!prop) | ||||||
|  | 		return; /* Not present, assumed ok */ | ||||||
|  |  | ||||||
|  | 	if (!data_is_one_string(prop->val)) | ||||||
|  | 		FAIL(c, dti, "\"%s\" property in %s is not a string", | ||||||
|  | 		     propname, node->fullpath); | ||||||
|  | } | ||||||
|  | #define WARNING_IF_NOT_STRING(nm, propname) \ | ||||||
|  | 	WARNING(nm, check_is_string, (propname)) | ||||||
|  | #define ERROR_IF_NOT_STRING(nm, propname) \ | ||||||
|  | 	ERROR(nm, check_is_string, (propname)) | ||||||
|  |  | ||||||
|  | static void check_is_cell(struct check *c, struct dt_info *dti, | ||||||
|  | 			  struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	char *propname = c->data; | ||||||
|  |  | ||||||
|  | 	prop = get_property(node, propname); | ||||||
|  | 	if (!prop) | ||||||
|  | 		return; /* Not present, assumed ok */ | ||||||
|  |  | ||||||
|  | 	if (prop->val.len != sizeof(cell_t)) | ||||||
|  | 		FAIL(c, dti, "\"%s\" property in %s is not a single cell", | ||||||
|  | 		     propname, node->fullpath); | ||||||
|  | } | ||||||
|  | #define WARNING_IF_NOT_CELL(nm, propname) \ | ||||||
|  | 	WARNING(nm, check_is_cell, (propname)) | ||||||
|  | #define ERROR_IF_NOT_CELL(nm, propname) \ | ||||||
|  | 	ERROR(nm, check_is_cell, (propname)) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Structural check functions | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static void check_duplicate_node_names(struct check *c, struct dt_info *dti, | ||||||
|  | 				       struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *child, *child2; | ||||||
|  |  | ||||||
|  | 	for_each_child(node, child) | ||||||
|  | 		for (child2 = child->next_sibling; | ||||||
|  | 		     child2; | ||||||
|  | 		     child2 = child2->next_sibling) | ||||||
|  | 			if (streq(child->name, child2->name)) | ||||||
|  | 				FAIL(c, dti, "Duplicate node name %s", | ||||||
|  | 				     child->fullpath); | ||||||
|  | } | ||||||
|  | ERROR(duplicate_node_names, check_duplicate_node_names, NULL); | ||||||
|  |  | ||||||
|  | static void check_duplicate_property_names(struct check *c, struct dt_info *dti, | ||||||
|  | 					   struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop, *prop2; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		for (prop2 = prop->next; prop2; prop2 = prop2->next) { | ||||||
|  | 			if (prop2->deleted) | ||||||
|  | 				continue; | ||||||
|  | 			if (streq(prop->name, prop2->name)) | ||||||
|  | 				FAIL(c, dti, "Duplicate property name %s in %s", | ||||||
|  | 				     prop->name, node->fullpath); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ERROR(duplicate_property_names, check_duplicate_property_names, NULL); | ||||||
|  |  | ||||||
|  | #define LOWERCASE	"abcdefghijklmnopqrstuvwxyz" | ||||||
|  | #define UPPERCASE	"ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||||||
|  | #define DIGITS		"0123456789" | ||||||
|  | #define PROPNODECHARS	LOWERCASE UPPERCASE DIGITS ",._+*#?-" | ||||||
|  | #define PROPNODECHARSSTRICT	LOWERCASE UPPERCASE DIGITS ",-" | ||||||
|  |  | ||||||
|  | static void check_node_name_chars(struct check *c, struct dt_info *dti, | ||||||
|  | 				  struct node *node) | ||||||
|  | { | ||||||
|  | 	int n = strspn(node->name, c->data); | ||||||
|  |  | ||||||
|  | 	if (n < strlen(node->name)) | ||||||
|  | 		FAIL(c, dti, "Bad character '%c' in node %s", | ||||||
|  | 		     node->name[n], node->fullpath); | ||||||
|  | } | ||||||
|  | ERROR(node_name_chars, check_node_name_chars, PROPNODECHARS "@"); | ||||||
|  |  | ||||||
|  | static void check_node_name_chars_strict(struct check *c, struct dt_info *dti, | ||||||
|  | 					 struct node *node) | ||||||
|  | { | ||||||
|  | 	int n = strspn(node->name, c->data); | ||||||
|  |  | ||||||
|  | 	if (n < node->basenamelen) | ||||||
|  | 		FAIL(c, dti, "Character '%c' not recommended in node %s", | ||||||
|  | 		     node->name[n], node->fullpath); | ||||||
|  | } | ||||||
|  | CHECK(node_name_chars_strict, check_node_name_chars_strict, PROPNODECHARSSTRICT); | ||||||
|  |  | ||||||
|  | static void check_node_name_format(struct check *c, struct dt_info *dti, | ||||||
|  | 				   struct node *node) | ||||||
|  | { | ||||||
|  | 	if (strchr(get_unitname(node), '@')) | ||||||
|  | 		FAIL(c, dti, "Node %s has multiple '@' characters in name", | ||||||
|  | 		     node->fullpath); | ||||||
|  | } | ||||||
|  | ERROR(node_name_format, check_node_name_format, NULL, &node_name_chars); | ||||||
|  |  | ||||||
|  | static void check_unit_address_vs_reg(struct check *c, struct dt_info *dti, | ||||||
|  | 				      struct node *node) | ||||||
|  | { | ||||||
|  | 	const char *unitname = get_unitname(node); | ||||||
|  | 	struct property *prop = get_property(node, "reg"); | ||||||
|  |  | ||||||
|  | 	if (!prop) { | ||||||
|  | 		prop = get_property(node, "ranges"); | ||||||
|  | 		if (prop && !prop->val.len) | ||||||
|  | 			prop = NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (prop) { | ||||||
|  | 		if (!unitname[0]) | ||||||
|  | 			FAIL(c, dti, "Node %s has a reg or ranges property, but no unit name", | ||||||
|  | 			    node->fullpath); | ||||||
|  | 	} else { | ||||||
|  | 		if (unitname[0]) | ||||||
|  | 			FAIL(c, dti, "Node %s has a unit name, but no reg property", | ||||||
|  | 			    node->fullpath); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | WARNING(unit_address_vs_reg, check_unit_address_vs_reg, NULL); | ||||||
|  |  | ||||||
|  | static void check_property_name_chars(struct check *c, struct dt_info *dti, | ||||||
|  | 				      struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		int n = strspn(prop->name, c->data); | ||||||
|  |  | ||||||
|  | 		if (n < strlen(prop->name)) | ||||||
|  | 			FAIL(c, dti, "Bad character '%c' in property name \"%s\", node %s", | ||||||
|  | 			     prop->name[n], prop->name, node->fullpath); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ERROR(property_name_chars, check_property_name_chars, PROPNODECHARS); | ||||||
|  |  | ||||||
|  | static void check_property_name_chars_strict(struct check *c, | ||||||
|  | 					     struct dt_info *dti, | ||||||
|  | 					     struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		const char *name = prop->name; | ||||||
|  | 		int n = strspn(name, c->data); | ||||||
|  |  | ||||||
|  | 		if (n == strlen(prop->name)) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		/* Certain names are whitelisted */ | ||||||
|  | 		if (streq(name, "device_type")) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 		 * # is only allowed at the beginning of property names not counting | ||||||
|  | 		 * the vendor prefix. | ||||||
|  | 		 */ | ||||||
|  | 		if (name[n] == '#' && ((n == 0) || (name[n-1] == ','))) { | ||||||
|  | 			name += n + 1; | ||||||
|  | 			n = strspn(name, c->data); | ||||||
|  | 		} | ||||||
|  | 		if (n < strlen(name)) | ||||||
|  | 			FAIL(c, dti, "Character '%c' not recommended in property name \"%s\", node %s", | ||||||
|  | 			     name[n], prop->name, node->fullpath); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | CHECK(property_name_chars_strict, check_property_name_chars_strict, PROPNODECHARSSTRICT); | ||||||
|  |  | ||||||
|  | #define DESCLABEL_FMT	"%s%s%s%s%s" | ||||||
|  | #define DESCLABEL_ARGS(node,prop,mark)		\ | ||||||
|  | 	((mark) ? "value of " : ""),		\ | ||||||
|  | 	((prop) ? "'" : ""), \ | ||||||
|  | 	((prop) ? (prop)->name : ""), \ | ||||||
|  | 	((prop) ? "' in " : ""), (node)->fullpath | ||||||
|  |  | ||||||
|  | static void check_duplicate_label(struct check *c, struct dt_info *dti, | ||||||
|  | 				  const char *label, struct node *node, | ||||||
|  | 				  struct property *prop, struct marker *mark) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	struct node *othernode = NULL; | ||||||
|  | 	struct property *otherprop = NULL; | ||||||
|  | 	struct marker *othermark = NULL; | ||||||
|  |  | ||||||
|  | 	othernode = get_node_by_label(dt, label); | ||||||
|  |  | ||||||
|  | 	if (!othernode) | ||||||
|  | 		otherprop = get_property_by_label(dt, label, &othernode); | ||||||
|  | 	if (!othernode) | ||||||
|  | 		othermark = get_marker_label(dt, label, &othernode, | ||||||
|  | 					       &otherprop); | ||||||
|  |  | ||||||
|  | 	if (!othernode) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if ((othernode != node) || (otherprop != prop) || (othermark != mark)) | ||||||
|  | 		FAIL(c, dti, "Duplicate label '%s' on " DESCLABEL_FMT | ||||||
|  | 		     " and " DESCLABEL_FMT, | ||||||
|  | 		     label, DESCLABEL_ARGS(node, prop, mark), | ||||||
|  | 		     DESCLABEL_ARGS(othernode, otherprop, othermark)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void check_duplicate_label_node(struct check *c, struct dt_info *dti, | ||||||
|  | 				       struct node *node) | ||||||
|  | { | ||||||
|  | 	struct label *l; | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	for_each_label(node->labels, l) | ||||||
|  | 		check_duplicate_label(c, dti, l->label, node, NULL, NULL); | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		struct marker *m = prop->val.markers; | ||||||
|  |  | ||||||
|  | 		for_each_label(prop->labels, l) | ||||||
|  | 			check_duplicate_label(c, dti, l->label, node, prop, NULL); | ||||||
|  |  | ||||||
|  | 		for_each_marker_of_type(m, LABEL) | ||||||
|  | 			check_duplicate_label(c, dti, m->ref, node, prop, m); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ERROR(duplicate_label, check_duplicate_label_node, NULL); | ||||||
|  |  | ||||||
|  | static cell_t check_phandle_prop(struct check *c, struct dt_info *dti, | ||||||
|  | 				 struct node *node, const char *propname) | ||||||
|  | { | ||||||
|  | 	struct node *root = dti->dt; | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct marker *m; | ||||||
|  | 	cell_t phandle; | ||||||
|  |  | ||||||
|  | 	prop = get_property(node, propname); | ||||||
|  | 	if (!prop) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	if (prop->val.len != sizeof(cell_t)) { | ||||||
|  | 		FAIL(c, dti, "%s has bad length (%d) %s property", | ||||||
|  | 		     node->fullpath, prop->val.len, prop->name); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	m = prop->val.markers; | ||||||
|  | 	for_each_marker_of_type(m, REF_PHANDLE) { | ||||||
|  | 		assert(m->offset == 0); | ||||||
|  | 		if (node != get_node_by_ref(root, m->ref)) | ||||||
|  | 			/* "Set this node's phandle equal to some | ||||||
|  | 			 * other node's phandle".  That's nonsensical | ||||||
|  | 			 * by construction. */ { | ||||||
|  | 			FAIL(c, dti, "%s in %s is a reference to another node", | ||||||
|  | 			     prop->name, node->fullpath); | ||||||
|  | 		} | ||||||
|  | 		/* But setting this node's phandle equal to its own | ||||||
|  | 		 * phandle is allowed - that means allocate a unique | ||||||
|  | 		 * phandle for this node, even if it's not otherwise | ||||||
|  | 		 * referenced.  The value will be filled in later, so | ||||||
|  | 		 * we treat it as having no phandle data for now. */ | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	phandle = propval_cell(prop); | ||||||
|  |  | ||||||
|  | 	if ((phandle == 0) || (phandle == -1)) { | ||||||
|  | 		FAIL(c, dti, "%s has bad value (0x%x) in %s property", | ||||||
|  | 		     node->fullpath, phandle, prop->name); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return phandle; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void check_explicit_phandles(struct check *c, struct dt_info *dti, | ||||||
|  | 				    struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *root = dti->dt; | ||||||
|  | 	struct node *other; | ||||||
|  | 	cell_t phandle, linux_phandle; | ||||||
|  |  | ||||||
|  | 	/* Nothing should have assigned phandles yet */ | ||||||
|  | 	assert(!node->phandle); | ||||||
|  |  | ||||||
|  | 	phandle = check_phandle_prop(c, dti, node, "phandle"); | ||||||
|  |  | ||||||
|  | 	linux_phandle = check_phandle_prop(c, dti, node, "linux,phandle"); | ||||||
|  |  | ||||||
|  | 	if (!phandle && !linux_phandle) | ||||||
|  | 		/* No valid phandles; nothing further to check */ | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (linux_phandle && phandle && (phandle != linux_phandle)) | ||||||
|  | 		FAIL(c, dti, "%s has mismatching 'phandle' and 'linux,phandle'" | ||||||
|  | 		     " properties", node->fullpath); | ||||||
|  |  | ||||||
|  | 	if (linux_phandle && !phandle) | ||||||
|  | 		phandle = linux_phandle; | ||||||
|  |  | ||||||
|  | 	other = get_node_by_phandle(root, phandle); | ||||||
|  | 	if (other && (other != node)) { | ||||||
|  | 		FAIL(c, dti, "%s has duplicated phandle 0x%x (seen before at %s)", | ||||||
|  | 		     node->fullpath, phandle, other->fullpath); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	node->phandle = phandle; | ||||||
|  | } | ||||||
|  | ERROR(explicit_phandles, check_explicit_phandles, NULL); | ||||||
|  |  | ||||||
|  | static void check_name_properties(struct check *c, struct dt_info *dti, | ||||||
|  | 				  struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property **pp, *prop = NULL; | ||||||
|  |  | ||||||
|  | 	for (pp = &node->proplist; *pp; pp = &((*pp)->next)) | ||||||
|  | 		if (streq((*pp)->name, "name")) { | ||||||
|  | 			prop = *pp; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	if (!prop) | ||||||
|  | 		return; /* No name property, that's fine */ | ||||||
|  |  | ||||||
|  | 	if ((prop->val.len != node->basenamelen+1) | ||||||
|  | 	    || (memcmp(prop->val.val, node->name, node->basenamelen) != 0)) { | ||||||
|  | 		FAIL(c, dti, "\"name\" property in %s is incorrect (\"%s\" instead" | ||||||
|  | 		     " of base node name)", node->fullpath, prop->val.val); | ||||||
|  | 	} else { | ||||||
|  | 		/* The name property is correct, and therefore redundant. | ||||||
|  | 		 * Delete it */ | ||||||
|  | 		*pp = prop->next; | ||||||
|  | 		free(prop->name); | ||||||
|  | 		data_free(prop->val); | ||||||
|  | 		free(prop); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ERROR_IF_NOT_STRING(name_is_string, "name"); | ||||||
|  | ERROR(name_properties, check_name_properties, NULL, &name_is_string); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Reference fixup functions | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | static void fixup_phandle_references(struct check *c, struct dt_info *dti, | ||||||
|  | 				     struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		struct marker *m = prop->val.markers; | ||||||
|  | 		struct node *refnode; | ||||||
|  | 		cell_t phandle; | ||||||
|  |  | ||||||
|  | 		for_each_marker_of_type(m, REF_PHANDLE) { | ||||||
|  | 			assert(m->offset + sizeof(cell_t) <= prop->val.len); | ||||||
|  |  | ||||||
|  | 			refnode = get_node_by_ref(dt, m->ref); | ||||||
|  | 			if (! refnode) { | ||||||
|  | 				if (!(dti->dtsflags & DTSF_PLUGIN)) | ||||||
|  | 					FAIL(c, dti, "Reference to non-existent node or " | ||||||
|  | 							"label \"%s\"\n", m->ref); | ||||||
|  | 				else /* mark the entry as unresolved */ | ||||||
|  | 					*((cell_t *)(prop->val.val + m->offset)) = | ||||||
|  | 						cpu_to_fdt32(0xffffffff); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			phandle = get_node_phandle(dt, refnode); | ||||||
|  | 			*((cell_t *)(prop->val.val + m->offset)) = cpu_to_fdt32(phandle); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ERROR(phandle_references, fixup_phandle_references, NULL, | ||||||
|  |       &duplicate_node_names, &explicit_phandles); | ||||||
|  |  | ||||||
|  | static void fixup_path_references(struct check *c, struct dt_info *dti, | ||||||
|  | 				  struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		struct marker *m = prop->val.markers; | ||||||
|  | 		struct node *refnode; | ||||||
|  | 		char *path; | ||||||
|  |  | ||||||
|  | 		for_each_marker_of_type(m, REF_PATH) { | ||||||
|  | 			assert(m->offset <= prop->val.len); | ||||||
|  |  | ||||||
|  | 			refnode = get_node_by_ref(dt, m->ref); | ||||||
|  | 			if (!refnode) { | ||||||
|  | 				FAIL(c, dti, "Reference to non-existent node or label \"%s\"\n", | ||||||
|  | 				     m->ref); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			path = refnode->fullpath; | ||||||
|  | 			prop->val = data_insert_at_marker(prop->val, m, path, | ||||||
|  | 							  strlen(path) + 1); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ERROR(path_references, fixup_path_references, NULL, &duplicate_node_names); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Semantic checks | ||||||
|  |  */ | ||||||
|  | WARNING_IF_NOT_CELL(address_cells_is_cell, "#address-cells"); | ||||||
|  | WARNING_IF_NOT_CELL(size_cells_is_cell, "#size-cells"); | ||||||
|  | WARNING_IF_NOT_CELL(interrupt_cells_is_cell, "#interrupt-cells"); | ||||||
|  |  | ||||||
|  | WARNING_IF_NOT_STRING(device_type_is_string, "device_type"); | ||||||
|  | WARNING_IF_NOT_STRING(model_is_string, "model"); | ||||||
|  | WARNING_IF_NOT_STRING(status_is_string, "status"); | ||||||
|  |  | ||||||
|  | static void fixup_addr_size_cells(struct check *c, struct dt_info *dti, | ||||||
|  | 				  struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	node->addr_cells = -1; | ||||||
|  | 	node->size_cells = -1; | ||||||
|  |  | ||||||
|  | 	prop = get_property(node, "#address-cells"); | ||||||
|  | 	if (prop) | ||||||
|  | 		node->addr_cells = propval_cell(prop); | ||||||
|  |  | ||||||
|  | 	prop = get_property(node, "#size-cells"); | ||||||
|  | 	if (prop) | ||||||
|  | 		node->size_cells = propval_cell(prop); | ||||||
|  | } | ||||||
|  | WARNING(addr_size_cells, fixup_addr_size_cells, NULL, | ||||||
|  | 	&address_cells_is_cell, &size_cells_is_cell); | ||||||
|  |  | ||||||
|  | #define node_addr_cells(n) \ | ||||||
|  | 	(((n)->addr_cells == -1) ? 2 : (n)->addr_cells) | ||||||
|  | #define node_size_cells(n) \ | ||||||
|  | 	(((n)->size_cells == -1) ? 1 : (n)->size_cells) | ||||||
|  |  | ||||||
|  | static void check_reg_format(struct check *c, struct dt_info *dti, | ||||||
|  | 			     struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	int addr_cells, size_cells, entrylen; | ||||||
|  |  | ||||||
|  | 	prop = get_property(node, "reg"); | ||||||
|  | 	if (!prop) | ||||||
|  | 		return; /* No "reg", that's fine */ | ||||||
|  |  | ||||||
|  | 	if (!node->parent) { | ||||||
|  | 		FAIL(c, dti, "Root node has a \"reg\" property"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (prop->val.len == 0) | ||||||
|  | 		FAIL(c, dti, "\"reg\" property in %s is empty", node->fullpath); | ||||||
|  |  | ||||||
|  | 	addr_cells = node_addr_cells(node->parent); | ||||||
|  | 	size_cells = node_size_cells(node->parent); | ||||||
|  | 	entrylen = (addr_cells + size_cells) * sizeof(cell_t); | ||||||
|  |  | ||||||
|  | 	if (!entrylen || (prop->val.len % entrylen) != 0) | ||||||
|  | 		FAIL(c, dti, "\"reg\" property in %s has invalid length (%d bytes) " | ||||||
|  | 		     "(#address-cells == %d, #size-cells == %d)", | ||||||
|  | 		     node->fullpath, prop->val.len, addr_cells, size_cells); | ||||||
|  | } | ||||||
|  | WARNING(reg_format, check_reg_format, NULL, &addr_size_cells); | ||||||
|  |  | ||||||
|  | static void check_ranges_format(struct check *c, struct dt_info *dti, | ||||||
|  | 				struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	int c_addr_cells, p_addr_cells, c_size_cells, p_size_cells, entrylen; | ||||||
|  |  | ||||||
|  | 	prop = get_property(node, "ranges"); | ||||||
|  | 	if (!prop) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (!node->parent) { | ||||||
|  | 		FAIL(c, dti, "Root node has a \"ranges\" property"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	p_addr_cells = node_addr_cells(node->parent); | ||||||
|  | 	p_size_cells = node_size_cells(node->parent); | ||||||
|  | 	c_addr_cells = node_addr_cells(node); | ||||||
|  | 	c_size_cells = node_size_cells(node); | ||||||
|  | 	entrylen = (p_addr_cells + c_addr_cells + c_size_cells) * sizeof(cell_t); | ||||||
|  |  | ||||||
|  | 	if (prop->val.len == 0) { | ||||||
|  | 		if (p_addr_cells != c_addr_cells) | ||||||
|  | 			FAIL(c, dti, "%s has empty \"ranges\" property but its " | ||||||
|  | 			     "#address-cells (%d) differs from %s (%d)", | ||||||
|  | 			     node->fullpath, c_addr_cells, node->parent->fullpath, | ||||||
|  | 			     p_addr_cells); | ||||||
|  | 		if (p_size_cells != c_size_cells) | ||||||
|  | 			FAIL(c, dti, "%s has empty \"ranges\" property but its " | ||||||
|  | 			     "#size-cells (%d) differs from %s (%d)", | ||||||
|  | 			     node->fullpath, c_size_cells, node->parent->fullpath, | ||||||
|  | 			     p_size_cells); | ||||||
|  | 	} else if ((prop->val.len % entrylen) != 0) { | ||||||
|  | 		FAIL(c, dti, "\"ranges\" property in %s has invalid length (%d bytes) " | ||||||
|  | 		     "(parent #address-cells == %d, child #address-cells == %d, " | ||||||
|  | 		     "#size-cells == %d)", node->fullpath, prop->val.len, | ||||||
|  | 		     p_addr_cells, c_addr_cells, c_size_cells); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | WARNING(ranges_format, check_ranges_format, NULL, &addr_size_cells); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Style checks | ||||||
|  |  */ | ||||||
|  | static void check_avoid_default_addr_size(struct check *c, struct dt_info *dti, | ||||||
|  | 					  struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *reg, *ranges; | ||||||
|  |  | ||||||
|  | 	if (!node->parent) | ||||||
|  | 		return; /* Ignore root node */ | ||||||
|  |  | ||||||
|  | 	reg = get_property(node, "reg"); | ||||||
|  | 	ranges = get_property(node, "ranges"); | ||||||
|  |  | ||||||
|  | 	if (!reg && !ranges) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (node->parent->addr_cells == -1) | ||||||
|  | 		FAIL(c, dti, "Relying on default #address-cells value for %s", | ||||||
|  | 		     node->fullpath); | ||||||
|  |  | ||||||
|  | 	if (node->parent->size_cells == -1) | ||||||
|  | 		FAIL(c, dti, "Relying on default #size-cells value for %s", | ||||||
|  | 		     node->fullpath); | ||||||
|  | } | ||||||
|  | WARNING(avoid_default_addr_size, check_avoid_default_addr_size, NULL, | ||||||
|  | 	&addr_size_cells); | ||||||
|  |  | ||||||
|  | static void check_obsolete_chosen_interrupt_controller(struct check *c, | ||||||
|  | 						       struct dt_info *dti, | ||||||
|  | 						       struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	struct node *chosen; | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	if (node != dt) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	chosen = get_node_by_path(dt, "/chosen"); | ||||||
|  | 	if (!chosen) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	prop = get_property(chosen, "interrupt-controller"); | ||||||
|  | 	if (prop) | ||||||
|  | 		FAIL(c, dti, "/chosen has obsolete \"interrupt-controller\" " | ||||||
|  | 		     "property"); | ||||||
|  | } | ||||||
|  | WARNING(obsolete_chosen_interrupt_controller, | ||||||
|  | 	check_obsolete_chosen_interrupt_controller, NULL); | ||||||
|  |  | ||||||
|  | static struct check *check_table[] = { | ||||||
|  | 	&duplicate_node_names, &duplicate_property_names, | ||||||
|  | 	&node_name_chars, &node_name_format, &property_name_chars, | ||||||
|  | 	&name_is_string, &name_properties, | ||||||
|  |  | ||||||
|  | 	&duplicate_label, | ||||||
|  |  | ||||||
|  | 	&explicit_phandles, | ||||||
|  | 	&phandle_references, &path_references, | ||||||
|  |  | ||||||
|  | 	&address_cells_is_cell, &size_cells_is_cell, &interrupt_cells_is_cell, | ||||||
|  | 	&device_type_is_string, &model_is_string, &status_is_string, | ||||||
|  |  | ||||||
|  | 	&property_name_chars_strict, | ||||||
|  | 	&node_name_chars_strict, | ||||||
|  |  | ||||||
|  | 	&addr_size_cells, ®_format, &ranges_format, | ||||||
|  |  | ||||||
|  | 	&unit_address_vs_reg, | ||||||
|  |  | ||||||
|  | 	&avoid_default_addr_size, | ||||||
|  | 	&obsolete_chosen_interrupt_controller, | ||||||
|  |  | ||||||
|  | 	&always_fail, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void enable_warning_error(struct check *c, bool warn, bool error) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	/* Raising level, also raise it for prereqs */ | ||||||
|  | 	if ((warn && !c->warn) || (error && !c->error)) | ||||||
|  | 		for (i = 0; i < c->num_prereqs; i++) | ||||||
|  | 			enable_warning_error(c->prereq[i], warn, error); | ||||||
|  |  | ||||||
|  | 	c->warn = c->warn || warn; | ||||||
|  | 	c->error = c->error || error; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void disable_warning_error(struct check *c, bool warn, bool error) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	/* Lowering level, also lower it for things this is the prereq | ||||||
|  | 	 * for */ | ||||||
|  | 	if ((warn && c->warn) || (error && c->error)) { | ||||||
|  | 		for (i = 0; i < ARRAY_SIZE(check_table); i++) { | ||||||
|  | 			struct check *cc = check_table[i]; | ||||||
|  | 			int j; | ||||||
|  |  | ||||||
|  | 			for (j = 0; j < cc->num_prereqs; j++) | ||||||
|  | 				if (cc->prereq[j] == c) | ||||||
|  | 					disable_warning_error(cc, warn, error); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	c->warn = c->warn && !warn; | ||||||
|  | 	c->error = c->error && !error; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void parse_checks_option(bool warn, bool error, const char *arg) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	const char *name = arg; | ||||||
|  | 	bool enable = true; | ||||||
|  |  | ||||||
|  | 	if ((strncmp(arg, "no-", 3) == 0) | ||||||
|  | 	    || (strncmp(arg, "no_", 3) == 0)) { | ||||||
|  | 		name = arg + 3; | ||||||
|  | 		enable = false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(check_table); i++) { | ||||||
|  | 		struct check *c = check_table[i]; | ||||||
|  |  | ||||||
|  | 		if (streq(c->name, name)) { | ||||||
|  | 			if (enable) | ||||||
|  | 				enable_warning_error(c, warn, error); | ||||||
|  | 			else | ||||||
|  | 				disable_warning_error(c, warn, error); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	die("Unrecognized check name \"%s\"\n", name); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void process_checks(bool force, struct dt_info *dti) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	int error = 0; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(check_table); i++) { | ||||||
|  | 		struct check *c = check_table[i]; | ||||||
|  |  | ||||||
|  | 		if (c->warn || c->error) | ||||||
|  | 			error = error || run_check(c, dti); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (error) { | ||||||
|  | 		if (!force) { | ||||||
|  | 			fprintf(stderr, "ERROR: Input tree has errors, aborting " | ||||||
|  | 				"(use -f to force output)\n"); | ||||||
|  | 			exit(2); | ||||||
|  | 		} else if (quiet < 3) { | ||||||
|  | 			fprintf(stderr, "Warning: Input tree has errors, " | ||||||
|  | 				"output forced\n"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										269
									
								
								scripts/dtc/data.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								scripts/dtc/data.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,269 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  |  | ||||||
|  | void data_free(struct data d) | ||||||
|  | { | ||||||
|  | 	struct marker *m, *nm; | ||||||
|  |  | ||||||
|  | 	m = d.markers; | ||||||
|  | 	while (m) { | ||||||
|  | 		nm = m->next; | ||||||
|  | 		free(m->ref); | ||||||
|  | 		free(m); | ||||||
|  | 		m = nm; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (d.val) | ||||||
|  | 		free(d.val); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_grow_for(struct data d, int xlen) | ||||||
|  | { | ||||||
|  | 	struct data nd; | ||||||
|  | 	int newsize; | ||||||
|  |  | ||||||
|  | 	if (xlen == 0) | ||||||
|  | 		return d; | ||||||
|  |  | ||||||
|  | 	nd = d; | ||||||
|  |  | ||||||
|  | 	newsize = xlen; | ||||||
|  |  | ||||||
|  | 	while ((d.len + xlen) > newsize) | ||||||
|  | 		newsize *= 2; | ||||||
|  |  | ||||||
|  | 	nd.val = xrealloc(d.val, newsize); | ||||||
|  |  | ||||||
|  | 	return nd; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_copy_mem(const char *mem, int len) | ||||||
|  | { | ||||||
|  | 	struct data d; | ||||||
|  |  | ||||||
|  | 	d = data_grow_for(empty_data, len); | ||||||
|  |  | ||||||
|  | 	d.len = len; | ||||||
|  | 	memcpy(d.val, mem, len); | ||||||
|  |  | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_copy_escape_string(const char *s, int len) | ||||||
|  | { | ||||||
|  | 	int i = 0; | ||||||
|  | 	struct data d; | ||||||
|  | 	char *q; | ||||||
|  |  | ||||||
|  | 	d = data_grow_for(empty_data, len + 1); | ||||||
|  |  | ||||||
|  | 	q = d.val; | ||||||
|  | 	while (i < len) { | ||||||
|  | 		char c = s[i++]; | ||||||
|  |  | ||||||
|  | 		if (c == '\\') | ||||||
|  | 			c = get_escape_char(s, &i); | ||||||
|  |  | ||||||
|  | 		q[d.len++] = c; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	q[d.len++] = '\0'; | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_copy_file(FILE *f, size_t maxlen) | ||||||
|  | { | ||||||
|  | 	struct data d = empty_data; | ||||||
|  |  | ||||||
|  | 	while (!feof(f) && (d.len < maxlen)) { | ||||||
|  | 		size_t chunksize, ret; | ||||||
|  |  | ||||||
|  | 		if (maxlen == -1) | ||||||
|  | 			chunksize = 4096; | ||||||
|  | 		else | ||||||
|  | 			chunksize = maxlen - d.len; | ||||||
|  |  | ||||||
|  | 		d = data_grow_for(d, chunksize); | ||||||
|  | 		ret = fread(d.val + d.len, 1, chunksize, f); | ||||||
|  |  | ||||||
|  | 		if (ferror(f)) | ||||||
|  | 			die("Error reading file into data: %s", strerror(errno)); | ||||||
|  |  | ||||||
|  | 		if (d.len + ret < d.len) | ||||||
|  | 			die("Overflow reading file into data\n"); | ||||||
|  |  | ||||||
|  | 		d.len += ret; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_data(struct data d, const void *p, int len) | ||||||
|  | { | ||||||
|  | 	d = data_grow_for(d, len); | ||||||
|  | 	memcpy(d.val + d.len, p, len); | ||||||
|  | 	d.len += len; | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_insert_at_marker(struct data d, struct marker *m, | ||||||
|  | 				  const void *p, int len) | ||||||
|  | { | ||||||
|  | 	d = data_grow_for(d, len); | ||||||
|  | 	memmove(d.val + m->offset + len, d.val + m->offset, d.len - m->offset); | ||||||
|  | 	memcpy(d.val + m->offset, p, len); | ||||||
|  | 	d.len += len; | ||||||
|  |  | ||||||
|  | 	/* Adjust all markers after the one we're inserting at */ | ||||||
|  | 	m = m->next; | ||||||
|  | 	for_each_marker(m) | ||||||
|  | 		m->offset += len; | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct data data_append_markers(struct data d, struct marker *m) | ||||||
|  | { | ||||||
|  | 	struct marker **mp = &d.markers; | ||||||
|  |  | ||||||
|  | 	/* Find the end of the markerlist */ | ||||||
|  | 	while (*mp) | ||||||
|  | 		mp = &((*mp)->next); | ||||||
|  | 	*mp = m; | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_merge(struct data d1, struct data d2) | ||||||
|  | { | ||||||
|  | 	struct data d; | ||||||
|  | 	struct marker *m2 = d2.markers; | ||||||
|  |  | ||||||
|  | 	d = data_append_markers(data_append_data(d1, d2.val, d2.len), m2); | ||||||
|  |  | ||||||
|  | 	/* Adjust for the length of d1 */ | ||||||
|  | 	for_each_marker(m2) | ||||||
|  | 		m2->offset += d1.len; | ||||||
|  |  | ||||||
|  | 	d2.markers = NULL; /* So data_free() doesn't clobber them */ | ||||||
|  | 	data_free(d2); | ||||||
|  |  | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_integer(struct data d, uint64_t value, int bits) | ||||||
|  | { | ||||||
|  | 	uint8_t value_8; | ||||||
|  | 	uint16_t value_16; | ||||||
|  | 	uint32_t value_32; | ||||||
|  | 	uint64_t value_64; | ||||||
|  |  | ||||||
|  | 	switch (bits) { | ||||||
|  | 	case 8: | ||||||
|  | 		value_8 = value; | ||||||
|  | 		return data_append_data(d, &value_8, 1); | ||||||
|  |  | ||||||
|  | 	case 16: | ||||||
|  | 		value_16 = cpu_to_fdt16(value); | ||||||
|  | 		return data_append_data(d, &value_16, 2); | ||||||
|  |  | ||||||
|  | 	case 32: | ||||||
|  | 		value_32 = cpu_to_fdt32(value); | ||||||
|  | 		return data_append_data(d, &value_32, 4); | ||||||
|  |  | ||||||
|  | 	case 64: | ||||||
|  | 		value_64 = cpu_to_fdt64(value); | ||||||
|  | 		return data_append_data(d, &value_64, 8); | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		die("Invalid literal size (%d)\n", bits); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_re(struct data d, const struct fdt_reserve_entry *re) | ||||||
|  | { | ||||||
|  | 	struct fdt_reserve_entry bere; | ||||||
|  |  | ||||||
|  | 	bere.address = cpu_to_fdt64(re->address); | ||||||
|  | 	bere.size = cpu_to_fdt64(re->size); | ||||||
|  |  | ||||||
|  | 	return data_append_data(d, &bere, sizeof(bere)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_cell(struct data d, cell_t word) | ||||||
|  | { | ||||||
|  | 	return data_append_integer(d, word, sizeof(word) * 8); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_addr(struct data d, uint64_t addr) | ||||||
|  | { | ||||||
|  | 	return data_append_integer(d, addr, sizeof(addr) * 8); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_byte(struct data d, uint8_t byte) | ||||||
|  | { | ||||||
|  | 	return data_append_data(d, &byte, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_zeroes(struct data d, int len) | ||||||
|  | { | ||||||
|  | 	d = data_grow_for(d, len); | ||||||
|  |  | ||||||
|  | 	memset(d.val + d.len, 0, len); | ||||||
|  | 	d.len += len; | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_append_align(struct data d, int align) | ||||||
|  | { | ||||||
|  | 	int newlen = ALIGN(d.len, align); | ||||||
|  | 	return data_append_zeroes(d, newlen - d.len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct data data_add_marker(struct data d, enum markertype type, char *ref) | ||||||
|  | { | ||||||
|  | 	struct marker *m; | ||||||
|  |  | ||||||
|  | 	m = xmalloc(sizeof(*m)); | ||||||
|  | 	m->offset = d.len; | ||||||
|  | 	m->type = type; | ||||||
|  | 	m->ref = ref; | ||||||
|  | 	m->next = NULL; | ||||||
|  |  | ||||||
|  | 	return data_append_markers(d, m); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool data_is_one_string(struct data d) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	int len = d.len; | ||||||
|  |  | ||||||
|  | 	if (len == 0) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < len-1; i++) | ||||||
|  | 		if (d.val[i] == '\0') | ||||||
|  | 			return false; | ||||||
|  |  | ||||||
|  | 	if (d.val[len-1] != '\0') | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
							
								
								
									
										311
									
								
								scripts/dtc/dtc-lexer.l
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								scripts/dtc/dtc-lexer.l
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,311 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | %option noyywrap nounput noinput never-interactive | ||||||
|  |  | ||||||
|  | %x BYTESTRING | ||||||
|  | %x PROPNODENAME | ||||||
|  | %s V1 | ||||||
|  |  | ||||||
|  | PROPNODECHAR	[a-zA-Z0-9,._+*#?@-] | ||||||
|  | PATHCHAR	({PROPNODECHAR}|[/]) | ||||||
|  | LABEL		[a-zA-Z_][a-zA-Z0-9_]* | ||||||
|  | STRING		\"([^\\"]|\\.)*\" | ||||||
|  | CHAR_LITERAL	'([^']|\\')*' | ||||||
|  | WS		[[:space:]] | ||||||
|  | COMMENT		"/*"([^*]|\*+[^*/])*\*+"/" | ||||||
|  | LINECOMMENT	"//".*\n | ||||||
|  |  | ||||||
|  | %{ | ||||||
|  | #include "dtc.h" | ||||||
|  | #include "srcpos.h" | ||||||
|  | #include "dtc-parser.tab.h" | ||||||
|  |  | ||||||
|  | YYLTYPE yylloc; | ||||||
|  | extern bool treesource_error; | ||||||
|  |  | ||||||
|  | /* CAUTION: this will stop working if we ever use yyless() or yyunput() */ | ||||||
|  | #define	YY_USER_ACTION \ | ||||||
|  | 	{ \ | ||||||
|  | 		srcpos_update(&yylloc, yytext, yyleng); \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | /*#define LEXDEBUG	1*/ | ||||||
|  |  | ||||||
|  | #ifdef LEXDEBUG | ||||||
|  | #define DPRINT(fmt, ...)	fprintf(stderr, fmt, ##__VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define DPRINT(fmt, ...)	do { } while (0) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | static int dts_version = 1; | ||||||
|  |  | ||||||
|  | #define BEGIN_DEFAULT()		DPRINT("<V1>\n"); \ | ||||||
|  | 				BEGIN(V1); \ | ||||||
|  |  | ||||||
|  | static void push_input_file(const char *filename); | ||||||
|  | static bool pop_input_file(void); | ||||||
|  | #ifdef __GNUC__ | ||||||
|  | static void lexical_error(const char *fmt, ...) | ||||||
|  | 	__attribute__((format (printf, 1, 2))); | ||||||
|  | #else | ||||||
|  | static void lexical_error(const char *fmt, ...); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | %} | ||||||
|  |  | ||||||
|  | %% | ||||||
|  | <*>"/include/"{WS}*{STRING} { | ||||||
|  | 			char *name = strchr(yytext, '\"') + 1; | ||||||
|  | 			yytext[yyleng-1] = '\0'; | ||||||
|  | 			push_input_file(name); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>^"#"(line)?[ \t]+[0-9]+[ \t]+{STRING}([ \t]+[0-9]+)? { | ||||||
|  | 			char *line, *fnstart, *fnend; | ||||||
|  | 			struct data fn; | ||||||
|  | 			/* skip text before line # */ | ||||||
|  | 			line = yytext; | ||||||
|  | 			while (!isdigit((unsigned char)*line)) | ||||||
|  | 				line++; | ||||||
|  |  | ||||||
|  | 			/* regexp ensures that first and list " | ||||||
|  | 			 * in the whole yytext are those at | ||||||
|  | 			 * beginning and end of the filename string */ | ||||||
|  | 			fnstart = memchr(yytext, '"', yyleng); | ||||||
|  | 			for (fnend = yytext + yyleng - 1; | ||||||
|  | 			     *fnend != '"'; fnend--) | ||||||
|  | 				; | ||||||
|  | 			assert(fnstart && fnend && (fnend > fnstart)); | ||||||
|  |  | ||||||
|  | 			fn = data_copy_escape_string(fnstart + 1, | ||||||
|  | 						     fnend - fnstart - 1); | ||||||
|  |  | ||||||
|  | 			/* Don't allow nuls in filenames */ | ||||||
|  | 			if (memchr(fn.val, '\0', fn.len - 1)) | ||||||
|  | 				lexical_error("nul in line number directive"); | ||||||
|  |  | ||||||
|  | 			/* -1 since #line is the number of the next line */ | ||||||
|  | 			srcpos_set_line(xstrdup(fn.val), atoi(line) - 1); | ||||||
|  | 			data_free(fn); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*><<EOF>>		{ | ||||||
|  | 			if (!pop_input_file()) { | ||||||
|  | 				yyterminate(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>{STRING}	{ | ||||||
|  | 			DPRINT("String: %s\n", yytext); | ||||||
|  | 			yylval.data = data_copy_escape_string(yytext+1, | ||||||
|  | 					yyleng-2); | ||||||
|  | 			return DT_STRING; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>"/dts-v1/"	{ | ||||||
|  | 			DPRINT("Keyword: /dts-v1/\n"); | ||||||
|  | 			dts_version = 1; | ||||||
|  | 			BEGIN_DEFAULT(); | ||||||
|  | 			return DT_V1; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>"/plugin/"	{ | ||||||
|  | 			DPRINT("Keyword: /plugin/\n"); | ||||||
|  | 			return DT_PLUGIN; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>"/memreserve/"	{ | ||||||
|  | 			DPRINT("Keyword: /memreserve/\n"); | ||||||
|  | 			BEGIN_DEFAULT(); | ||||||
|  | 			return DT_MEMRESERVE; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>"/bits/"	{ | ||||||
|  | 			DPRINT("Keyword: /bits/\n"); | ||||||
|  | 			BEGIN_DEFAULT(); | ||||||
|  | 			return DT_BITS; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>"/delete-property/"	{ | ||||||
|  | 			DPRINT("Keyword: /delete-property/\n"); | ||||||
|  | 			DPRINT("<PROPNODENAME>\n"); | ||||||
|  | 			BEGIN(PROPNODENAME); | ||||||
|  | 			return DT_DEL_PROP; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>"/delete-node/"	{ | ||||||
|  | 			DPRINT("Keyword: /delete-node/\n"); | ||||||
|  | 			DPRINT("<PROPNODENAME>\n"); | ||||||
|  | 			BEGIN(PROPNODENAME); | ||||||
|  | 			return DT_DEL_NODE; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>{LABEL}:	{ | ||||||
|  | 			DPRINT("Label: %s\n", yytext); | ||||||
|  | 			yylval.labelref = xstrdup(yytext); | ||||||
|  | 			yylval.labelref[yyleng-1] = '\0'; | ||||||
|  | 			return DT_LABEL; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <V1>([0-9]+|0[xX][0-9a-fA-F]+)(U|L|UL|LL|ULL)? { | ||||||
|  | 			char *e; | ||||||
|  | 			DPRINT("Integer Literal: '%s'\n", yytext); | ||||||
|  |  | ||||||
|  | 			errno = 0; | ||||||
|  | 			yylval.integer = strtoull(yytext, &e, 0); | ||||||
|  |  | ||||||
|  | 			if (*e && e[strspn(e, "UL")]) { | ||||||
|  | 				lexical_error("Bad integer literal '%s'", | ||||||
|  | 					      yytext); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if (errno == ERANGE) | ||||||
|  | 				lexical_error("Integer literal '%s' out of range", | ||||||
|  | 					      yytext); | ||||||
|  | 			else | ||||||
|  | 				/* ERANGE is the only strtoull error triggerable | ||||||
|  | 				 *  by strings matching the pattern */ | ||||||
|  | 				assert(errno == 0); | ||||||
|  | 			return DT_LITERAL; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>{CHAR_LITERAL}	{ | ||||||
|  | 			struct data d; | ||||||
|  | 			DPRINT("Character literal: %s\n", yytext); | ||||||
|  |  | ||||||
|  | 			d = data_copy_escape_string(yytext+1, yyleng-2); | ||||||
|  | 			if (d.len == 1) { | ||||||
|  | 				lexical_error("Empty character literal"); | ||||||
|  | 				yylval.integer = 0; | ||||||
|  | 			} else { | ||||||
|  | 				yylval.integer = (unsigned char)d.val[0]; | ||||||
|  |  | ||||||
|  | 				if (d.len > 2) | ||||||
|  | 					lexical_error("Character literal has %d" | ||||||
|  | 						      " characters instead of 1", | ||||||
|  | 						      d.len - 1); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			data_free(d); | ||||||
|  | 			return DT_CHAR_LITERAL; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>\&{LABEL}	{	/* label reference */ | ||||||
|  | 			DPRINT("Ref: %s\n", yytext+1); | ||||||
|  | 			yylval.labelref = xstrdup(yytext+1); | ||||||
|  | 			return DT_REF; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>"&{/"{PATHCHAR}*\}	{	/* new-style path reference */ | ||||||
|  | 			yytext[yyleng-1] = '\0'; | ||||||
|  | 			DPRINT("Ref: %s\n", yytext+2); | ||||||
|  | 			yylval.labelref = xstrdup(yytext+2); | ||||||
|  | 			return DT_REF; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <BYTESTRING>[0-9a-fA-F]{2} { | ||||||
|  | 			yylval.byte = strtol(yytext, NULL, 16); | ||||||
|  | 			DPRINT("Byte: %02x\n", (int)yylval.byte); | ||||||
|  | 			return DT_BYTE; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <BYTESTRING>"]"	{ | ||||||
|  | 			DPRINT("/BYTESTRING\n"); | ||||||
|  | 			BEGIN_DEFAULT(); | ||||||
|  | 			return ']'; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <PROPNODENAME>\\?{PROPNODECHAR}+ { | ||||||
|  | 			DPRINT("PropNodeName: %s\n", yytext); | ||||||
|  | 			yylval.propnodename = xstrdup((yytext[0] == '\\') ? | ||||||
|  | 							yytext + 1 : yytext); | ||||||
|  | 			BEGIN_DEFAULT(); | ||||||
|  | 			return DT_PROPNODENAME; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | "/incbin/"	{ | ||||||
|  | 			DPRINT("Binary Include\n"); | ||||||
|  | 			return DT_INCBIN; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | <*>{WS}+	/* eat whitespace */ | ||||||
|  | <*>{COMMENT}+	/* eat C-style comments */ | ||||||
|  | <*>{LINECOMMENT}+ /* eat C++-style comments */ | ||||||
|  |  | ||||||
|  | <*>"<<"		{ return DT_LSHIFT; }; | ||||||
|  | <*>">>"		{ return DT_RSHIFT; }; | ||||||
|  | <*>"<="		{ return DT_LE; }; | ||||||
|  | <*>">="		{ return DT_GE; }; | ||||||
|  | <*>"=="		{ return DT_EQ; }; | ||||||
|  | <*>"!="		{ return DT_NE; }; | ||||||
|  | <*>"&&"		{ return DT_AND; }; | ||||||
|  | <*>"||"		{ return DT_OR; }; | ||||||
|  |  | ||||||
|  | <*>.		{ | ||||||
|  | 			DPRINT("Char: %c (\\x%02x)\n", yytext[0], | ||||||
|  | 				(unsigned)yytext[0]); | ||||||
|  | 			if (yytext[0] == '[') { | ||||||
|  | 				DPRINT("<BYTESTRING>\n"); | ||||||
|  | 				BEGIN(BYTESTRING); | ||||||
|  | 			} | ||||||
|  | 			if ((yytext[0] == '{') | ||||||
|  | 			    || (yytext[0] == ';')) { | ||||||
|  | 				DPRINT("<PROPNODENAME>\n"); | ||||||
|  | 				BEGIN(PROPNODENAME); | ||||||
|  | 			} | ||||||
|  | 			return yytext[0]; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | %% | ||||||
|  |  | ||||||
|  | static void push_input_file(const char *filename) | ||||||
|  | { | ||||||
|  | 	assert(filename); | ||||||
|  |  | ||||||
|  | 	srcfile_push(filename); | ||||||
|  |  | ||||||
|  | 	yyin = current_srcfile->f; | ||||||
|  |  | ||||||
|  | 	yypush_buffer_state(yy_create_buffer(yyin, YY_BUF_SIZE)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static bool pop_input_file(void) | ||||||
|  | { | ||||||
|  | 	if (srcfile_pop() == 0) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	yypop_buffer_state(); | ||||||
|  | 	yyin = current_srcfile->f; | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void lexical_error(const char *fmt, ...) | ||||||
|  | { | ||||||
|  | 	va_list ap; | ||||||
|  |  | ||||||
|  | 	va_start(ap, fmt); | ||||||
|  | 	srcpos_verror(&yylloc, "Lexical error", fmt, ap); | ||||||
|  | 	va_end(ap); | ||||||
|  |  | ||||||
|  | 	treesource_error = true; | ||||||
|  | } | ||||||
							
								
								
									
										2260
									
								
								scripts/dtc/dtc-lexer.lex.c_shipped
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2260
									
								
								scripts/dtc/dtc-lexer.lex.c_shipped
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2301
									
								
								scripts/dtc/dtc-parser.tab.c_shipped
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2301
									
								
								scripts/dtc/dtc-parser.tab.c_shipped
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										123
									
								
								scripts/dtc/dtc-parser.tab.h_shipped
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								scripts/dtc/dtc-parser.tab.h_shipped
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | |||||||
|  | /* A Bison parser, made by GNU Bison 3.0.2.  */ | ||||||
|  |  | ||||||
|  | /* Bison interface for Yacc-like parsers in C | ||||||
|  |  | ||||||
|  |    Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. | ||||||
|  |  | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  |  | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU General Public License for more details. | ||||||
|  |  | ||||||
|  |    You should have received a copy of the GNU General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | ||||||
|  |  | ||||||
|  | /* As a special exception, you may create a larger work that contains | ||||||
|  |    part or all of the Bison parser skeleton and distribute that work | ||||||
|  |    under terms of your choice, so long as that work isn't itself a | ||||||
|  |    parser generator using the skeleton or a modified version thereof | ||||||
|  |    as a parser skeleton.  Alternatively, if you modify or redistribute | ||||||
|  |    the parser skeleton itself, you may (at your option) remove this | ||||||
|  |    special exception, which will cause the skeleton and the resulting | ||||||
|  |    Bison output files to be licensed under the GNU General Public | ||||||
|  |    License without this special exception. | ||||||
|  |  | ||||||
|  |    This special exception was added by the Free Software Foundation in | ||||||
|  |    version 2.2 of Bison.  */ | ||||||
|  |  | ||||||
|  | #ifndef YY_YY_DTC_PARSER_TAB_H_INCLUDED | ||||||
|  | # define YY_YY_DTC_PARSER_TAB_H_INCLUDED | ||||||
|  | /* Debug traces.  */ | ||||||
|  | #ifndef YYDEBUG | ||||||
|  | # define YYDEBUG 0 | ||||||
|  | #endif | ||||||
|  | #if YYDEBUG | ||||||
|  | extern int yydebug; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Token type.  */ | ||||||
|  | #ifndef YYTOKENTYPE | ||||||
|  | # define YYTOKENTYPE | ||||||
|  |   enum yytokentype | ||||||
|  |   { | ||||||
|  |     DT_V1 = 258, | ||||||
|  |     DT_PLUGIN = 259, | ||||||
|  |     DT_MEMRESERVE = 260, | ||||||
|  |     DT_LSHIFT = 261, | ||||||
|  |     DT_RSHIFT = 262, | ||||||
|  |     DT_LE = 263, | ||||||
|  |     DT_GE = 264, | ||||||
|  |     DT_EQ = 265, | ||||||
|  |     DT_NE = 266, | ||||||
|  |     DT_AND = 267, | ||||||
|  |     DT_OR = 268, | ||||||
|  |     DT_BITS = 269, | ||||||
|  |     DT_DEL_PROP = 270, | ||||||
|  |     DT_DEL_NODE = 271, | ||||||
|  |     DT_PROPNODENAME = 272, | ||||||
|  |     DT_LITERAL = 273, | ||||||
|  |     DT_CHAR_LITERAL = 274, | ||||||
|  |     DT_BYTE = 275, | ||||||
|  |     DT_STRING = 276, | ||||||
|  |     DT_LABEL = 277, | ||||||
|  |     DT_REF = 278, | ||||||
|  |     DT_INCBIN = 279 | ||||||
|  |   }; | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Value type.  */ | ||||||
|  | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED | ||||||
|  | typedef union YYSTYPE YYSTYPE; | ||||||
|  | union YYSTYPE | ||||||
|  | { | ||||||
|  | #line 39 "dtc-parser.y" /* yacc.c:1909  */ | ||||||
|  |  | ||||||
|  | 	char *propnodename; | ||||||
|  | 	char *labelref; | ||||||
|  | 	uint8_t byte; | ||||||
|  | 	struct data data; | ||||||
|  |  | ||||||
|  | 	struct { | ||||||
|  | 		struct data	data; | ||||||
|  | 		int		bits; | ||||||
|  | 	} array; | ||||||
|  |  | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct property *proplist; | ||||||
|  | 	struct node *node; | ||||||
|  | 	struct node *nodelist; | ||||||
|  | 	struct reserve_info *re; | ||||||
|  | 	uint64_t integer; | ||||||
|  | 	unsigned int flags; | ||||||
|  |  | ||||||
|  | #line 99 "dtc-parser.tab.h" /* yacc.c:1909  */ | ||||||
|  | }; | ||||||
|  | # define YYSTYPE_IS_TRIVIAL 1 | ||||||
|  | # define YYSTYPE_IS_DECLARED 1 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* Location type.  */ | ||||||
|  | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED | ||||||
|  | typedef struct YYLTYPE YYLTYPE; | ||||||
|  | struct YYLTYPE | ||||||
|  | { | ||||||
|  |   int first_line; | ||||||
|  |   int first_column; | ||||||
|  |   int last_line; | ||||||
|  |   int last_column; | ||||||
|  | }; | ||||||
|  | # define YYLTYPE_IS_DECLARED 1 | ||||||
|  | # define YYLTYPE_IS_TRIVIAL 1 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | extern YYSTYPE yylval; | ||||||
|  | extern YYLTYPE yylloc; | ||||||
|  | int yyparse (void); | ||||||
|  |  | ||||||
|  | #endif /* !YY_YY_DTC_PARSER_TAB_H_INCLUDED  */ | ||||||
							
								
								
									
										519
									
								
								scripts/dtc/dtc-parser.y
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										519
									
								
								scripts/dtc/dtc-parser.y
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,519 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  | %{ | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <inttypes.h> | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  | #include "srcpos.h" | ||||||
|  |  | ||||||
|  | extern int yylex(void); | ||||||
|  | extern void yyerror(char const *s); | ||||||
|  | #define ERROR(loc, ...) \ | ||||||
|  | 	do { \ | ||||||
|  | 		srcpos_error((loc), "Error", __VA_ARGS__); \ | ||||||
|  | 		treesource_error = true; \ | ||||||
|  | 	} while (0) | ||||||
|  |  | ||||||
|  | extern struct dt_info *parser_output; | ||||||
|  | extern bool treesource_error; | ||||||
|  | %} | ||||||
|  |  | ||||||
|  | %union { | ||||||
|  | 	char *propnodename; | ||||||
|  | 	char *labelref; | ||||||
|  | 	uint8_t byte; | ||||||
|  | 	struct data data; | ||||||
|  |  | ||||||
|  | 	struct { | ||||||
|  | 		struct data	data; | ||||||
|  | 		int		bits; | ||||||
|  | 	} array; | ||||||
|  |  | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct property *proplist; | ||||||
|  | 	struct node *node; | ||||||
|  | 	struct node *nodelist; | ||||||
|  | 	struct reserve_info *re; | ||||||
|  | 	uint64_t integer; | ||||||
|  | 	unsigned int flags; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | %token DT_V1 | ||||||
|  | %token DT_PLUGIN | ||||||
|  | %token DT_MEMRESERVE | ||||||
|  | %token DT_LSHIFT DT_RSHIFT DT_LE DT_GE DT_EQ DT_NE DT_AND DT_OR | ||||||
|  | %token DT_BITS | ||||||
|  | %token DT_DEL_PROP | ||||||
|  | %token DT_DEL_NODE | ||||||
|  | %token <propnodename> DT_PROPNODENAME | ||||||
|  | %token <integer> DT_LITERAL | ||||||
|  | %token <integer> DT_CHAR_LITERAL | ||||||
|  | %token <byte> DT_BYTE | ||||||
|  | %token <data> DT_STRING | ||||||
|  | %token <labelref> DT_LABEL | ||||||
|  | %token <labelref> DT_REF | ||||||
|  | %token DT_INCBIN | ||||||
|  |  | ||||||
|  | %type <data> propdata | ||||||
|  | %type <data> propdataprefix | ||||||
|  | %type <flags> header | ||||||
|  | %type <flags> headers | ||||||
|  | %type <re> memreserve | ||||||
|  | %type <re> memreserves | ||||||
|  | %type <array> arrayprefix | ||||||
|  | %type <data> bytestring | ||||||
|  | %type <prop> propdef | ||||||
|  | %type <proplist> proplist | ||||||
|  |  | ||||||
|  | %type <node> devicetree | ||||||
|  | %type <node> nodedef | ||||||
|  | %type <node> subnode | ||||||
|  | %type <nodelist> subnodes | ||||||
|  |  | ||||||
|  | %type <integer> integer_prim | ||||||
|  | %type <integer> integer_unary | ||||||
|  | %type <integer> integer_mul | ||||||
|  | %type <integer> integer_add | ||||||
|  | %type <integer> integer_shift | ||||||
|  | %type <integer> integer_rela | ||||||
|  | %type <integer> integer_eq | ||||||
|  | %type <integer> integer_bitand | ||||||
|  | %type <integer> integer_bitxor | ||||||
|  | %type <integer> integer_bitor | ||||||
|  | %type <integer> integer_and | ||||||
|  | %type <integer> integer_or | ||||||
|  | %type <integer> integer_trinary | ||||||
|  | %type <integer> integer_expr | ||||||
|  |  | ||||||
|  | %% | ||||||
|  |  | ||||||
|  | sourcefile: | ||||||
|  | 	  headers memreserves devicetree | ||||||
|  | 		{ | ||||||
|  | 			parser_output = build_dt_info($1, $2, $3, | ||||||
|  | 			                              guess_boot_cpuid($3)); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | header: | ||||||
|  | 	  DT_V1 ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = DTSF_V1; | ||||||
|  | 		} | ||||||
|  | 	| DT_V1 ';' DT_PLUGIN ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = DTSF_V1 | DTSF_PLUGIN; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | headers: | ||||||
|  | 	  header | ||||||
|  | 	| header headers | ||||||
|  | 		{ | ||||||
|  | 			if ($2 != $1) | ||||||
|  | 				ERROR(&@2, "Header flags don't match earlier ones"); | ||||||
|  | 			$$ = $1; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | memreserves: | ||||||
|  | 	  /* empty */ | ||||||
|  | 		{ | ||||||
|  | 			$$ = NULL; | ||||||
|  | 		} | ||||||
|  | 	| memreserve memreserves | ||||||
|  | 		{ | ||||||
|  | 			$$ = chain_reserve_entry($1, $2); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | memreserve: | ||||||
|  | 	  DT_MEMRESERVE integer_prim integer_prim ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = build_reserve_entry($2, $3); | ||||||
|  | 		} | ||||||
|  | 	| DT_LABEL memreserve | ||||||
|  | 		{ | ||||||
|  | 			add_label(&$2->labels, $1); | ||||||
|  | 			$$ = $2; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | devicetree: | ||||||
|  | 	  '/' nodedef | ||||||
|  | 		{ | ||||||
|  | 			$$ = name_node($2, ""); | ||||||
|  | 		} | ||||||
|  | 	| devicetree '/' nodedef | ||||||
|  | 		{ | ||||||
|  | 			$$ = merge_nodes($1, $3); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	| devicetree DT_LABEL DT_REF nodedef | ||||||
|  | 		{ | ||||||
|  | 			struct node *target = get_node_by_ref($1, $3); | ||||||
|  |  | ||||||
|  | 			if (target) { | ||||||
|  | 				add_label(&target->labels, $2); | ||||||
|  | 				merge_nodes(target, $4); | ||||||
|  | 			} else | ||||||
|  | 				ERROR(&@3, "Label or path %s not found", $3); | ||||||
|  | 			$$ = $1; | ||||||
|  | 		} | ||||||
|  | 	| devicetree DT_REF nodedef | ||||||
|  | 		{ | ||||||
|  | 			struct node *target = get_node_by_ref($1, $2); | ||||||
|  |  | ||||||
|  | 			if (target) | ||||||
|  | 				merge_nodes(target, $3); | ||||||
|  | 			else | ||||||
|  | 				ERROR(&@2, "Label or path %s not found", $2); | ||||||
|  | 			$$ = $1; | ||||||
|  | 		} | ||||||
|  | 	| devicetree DT_DEL_NODE DT_REF ';' | ||||||
|  | 		{ | ||||||
|  | 			struct node *target = get_node_by_ref($1, $3); | ||||||
|  |  | ||||||
|  | 			if (target) | ||||||
|  | 				delete_node(target); | ||||||
|  | 			else | ||||||
|  | 				ERROR(&@3, "Label or path %s not found", $3); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 			$$ = $1; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | nodedef: | ||||||
|  | 	  '{' proplist subnodes '}' ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = build_node($2, $3); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | proplist: | ||||||
|  | 	  /* empty */ | ||||||
|  | 		{ | ||||||
|  | 			$$ = NULL; | ||||||
|  | 		} | ||||||
|  | 	| proplist propdef | ||||||
|  | 		{ | ||||||
|  | 			$$ = chain_property($2, $1); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | propdef: | ||||||
|  | 	  DT_PROPNODENAME '=' propdata ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = build_property($1, $3); | ||||||
|  | 		} | ||||||
|  | 	| DT_PROPNODENAME ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = build_property($1, empty_data); | ||||||
|  | 		} | ||||||
|  | 	| DT_DEL_PROP DT_PROPNODENAME ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = build_property_delete($2); | ||||||
|  | 		} | ||||||
|  | 	| DT_LABEL propdef | ||||||
|  | 		{ | ||||||
|  | 			add_label(&$2->labels, $1); | ||||||
|  | 			$$ = $2; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | propdata: | ||||||
|  | 	  propdataprefix DT_STRING | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_merge($1, $2); | ||||||
|  | 		} | ||||||
|  | 	| propdataprefix arrayprefix '>' | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_merge($1, $2.data); | ||||||
|  | 		} | ||||||
|  | 	| propdataprefix '[' bytestring ']' | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_merge($1, $3); | ||||||
|  | 		} | ||||||
|  | 	| propdataprefix DT_REF | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_add_marker($1, REF_PATH, $2); | ||||||
|  | 		} | ||||||
|  | 	| propdataprefix DT_INCBIN '(' DT_STRING ',' integer_prim ',' integer_prim ')' | ||||||
|  | 		{ | ||||||
|  | 			FILE *f = srcfile_relative_open($4.val, NULL); | ||||||
|  | 			struct data d; | ||||||
|  |  | ||||||
|  | 			if ($6 != 0) | ||||||
|  | 				if (fseek(f, $6, SEEK_SET) != 0) | ||||||
|  | 					die("Couldn't seek to offset %llu in \"%s\": %s", | ||||||
|  | 					    (unsigned long long)$6, $4.val, | ||||||
|  | 					    strerror(errno)); | ||||||
|  |  | ||||||
|  | 			d = data_copy_file(f, $8); | ||||||
|  |  | ||||||
|  | 			$$ = data_merge($1, d); | ||||||
|  | 			fclose(f); | ||||||
|  | 		} | ||||||
|  | 	| propdataprefix DT_INCBIN '(' DT_STRING ')' | ||||||
|  | 		{ | ||||||
|  | 			FILE *f = srcfile_relative_open($4.val, NULL); | ||||||
|  | 			struct data d = empty_data; | ||||||
|  |  | ||||||
|  | 			d = data_copy_file(f, -1); | ||||||
|  |  | ||||||
|  | 			$$ = data_merge($1, d); | ||||||
|  | 			fclose(f); | ||||||
|  | 		} | ||||||
|  | 	| propdata DT_LABEL | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_add_marker($1, LABEL, $2); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | propdataprefix: | ||||||
|  | 	  /* empty */ | ||||||
|  | 		{ | ||||||
|  | 			$$ = empty_data; | ||||||
|  | 		} | ||||||
|  | 	| propdata ',' | ||||||
|  | 		{ | ||||||
|  | 			$$ = $1; | ||||||
|  | 		} | ||||||
|  | 	| propdataprefix DT_LABEL | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_add_marker($1, LABEL, $2); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | arrayprefix: | ||||||
|  | 	DT_BITS DT_LITERAL '<' | ||||||
|  | 		{ | ||||||
|  | 			unsigned long long bits; | ||||||
|  |  | ||||||
|  | 			bits = $2; | ||||||
|  |  | ||||||
|  | 			if ((bits !=  8) && (bits != 16) && | ||||||
|  | 			    (bits != 32) && (bits != 64)) { | ||||||
|  | 				ERROR(&@2, "Array elements must be" | ||||||
|  | 				      " 8, 16, 32 or 64-bits"); | ||||||
|  | 				bits = 32; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			$$.data = empty_data; | ||||||
|  | 			$$.bits = bits; | ||||||
|  | 		} | ||||||
|  | 	| '<' | ||||||
|  | 		{ | ||||||
|  | 			$$.data = empty_data; | ||||||
|  | 			$$.bits = 32; | ||||||
|  | 		} | ||||||
|  | 	| arrayprefix integer_prim | ||||||
|  | 		{ | ||||||
|  | 			if ($1.bits < 64) { | ||||||
|  | 				uint64_t mask = (1ULL << $1.bits) - 1; | ||||||
|  | 				/* | ||||||
|  | 				 * Bits above mask must either be all zero | ||||||
|  | 				 * (positive within range of mask) or all one | ||||||
|  | 				 * (negative and sign-extended). The second | ||||||
|  | 				 * condition is true if when we set all bits | ||||||
|  | 				 * within the mask to one (i.e. | in the | ||||||
|  | 				 * mask), all bits are one. | ||||||
|  | 				 */ | ||||||
|  | 				if (($2 > mask) && (($2 | mask) != -1ULL)) | ||||||
|  | 					ERROR(&@2, "Value out of range for" | ||||||
|  | 					      " %d-bit array element", $1.bits); | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			$$.data = data_append_integer($1.data, $2, $1.bits); | ||||||
|  | 		} | ||||||
|  | 	| arrayprefix DT_REF | ||||||
|  | 		{ | ||||||
|  | 			uint64_t val = ~0ULL >> (64 - $1.bits); | ||||||
|  |  | ||||||
|  | 			if ($1.bits == 32) | ||||||
|  | 				$1.data = data_add_marker($1.data, | ||||||
|  | 							  REF_PHANDLE, | ||||||
|  | 							  $2); | ||||||
|  | 			else | ||||||
|  | 				ERROR(&@2, "References are only allowed in " | ||||||
|  | 					    "arrays with 32-bit elements."); | ||||||
|  |  | ||||||
|  | 			$$.data = data_append_integer($1.data, val, $1.bits); | ||||||
|  | 		} | ||||||
|  | 	| arrayprefix DT_LABEL | ||||||
|  | 		{ | ||||||
|  | 			$$.data = data_add_marker($1.data, LABEL, $2); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_prim: | ||||||
|  | 	  DT_LITERAL | ||||||
|  | 	| DT_CHAR_LITERAL | ||||||
|  | 	| '(' integer_expr ')' | ||||||
|  | 		{ | ||||||
|  | 			$$ = $2; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_expr: | ||||||
|  | 	integer_trinary | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_trinary: | ||||||
|  | 	  integer_or | ||||||
|  | 	| integer_or '?' integer_expr ':' integer_trinary { $$ = $1 ? $3 : $5; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_or: | ||||||
|  | 	  integer_and | ||||||
|  | 	| integer_or DT_OR integer_and { $$ = $1 || $3; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_and: | ||||||
|  | 	  integer_bitor | ||||||
|  | 	| integer_and DT_AND integer_bitor { $$ = $1 && $3; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_bitor: | ||||||
|  | 	  integer_bitxor | ||||||
|  | 	| integer_bitor '|' integer_bitxor { $$ = $1 | $3; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_bitxor: | ||||||
|  | 	  integer_bitand | ||||||
|  | 	| integer_bitxor '^' integer_bitand { $$ = $1 ^ $3; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_bitand: | ||||||
|  | 	  integer_eq | ||||||
|  | 	| integer_bitand '&' integer_eq { $$ = $1 & $3; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_eq: | ||||||
|  | 	  integer_rela | ||||||
|  | 	| integer_eq DT_EQ integer_rela { $$ = $1 == $3; } | ||||||
|  | 	| integer_eq DT_NE integer_rela { $$ = $1 != $3; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_rela: | ||||||
|  | 	  integer_shift | ||||||
|  | 	| integer_rela '<' integer_shift { $$ = $1 < $3; } | ||||||
|  | 	| integer_rela '>' integer_shift { $$ = $1 > $3; } | ||||||
|  | 	| integer_rela DT_LE integer_shift { $$ = $1 <= $3; } | ||||||
|  | 	| integer_rela DT_GE integer_shift { $$ = $1 >= $3; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_shift: | ||||||
|  | 	  integer_shift DT_LSHIFT integer_add { $$ = $1 << $3; } | ||||||
|  | 	| integer_shift DT_RSHIFT integer_add { $$ = $1 >> $3; } | ||||||
|  | 	| integer_add | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_add: | ||||||
|  | 	  integer_add '+' integer_mul { $$ = $1 + $3; } | ||||||
|  | 	| integer_add '-' integer_mul { $$ = $1 - $3; } | ||||||
|  | 	| integer_mul | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_mul: | ||||||
|  | 	  integer_mul '*' integer_unary { $$ = $1 * $3; } | ||||||
|  | 	| integer_mul '/' integer_unary | ||||||
|  | 		{ | ||||||
|  | 			if ($3 != 0) { | ||||||
|  | 				$$ = $1 / $3; | ||||||
|  | 			} else { | ||||||
|  | 				ERROR(&@$, "Division by zero"); | ||||||
|  | 				$$ = 0; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	| integer_mul '%' integer_unary | ||||||
|  | 		{ | ||||||
|  | 			if ($3 != 0) { | ||||||
|  | 				$$ = $1 % $3; | ||||||
|  | 			} else { | ||||||
|  | 				ERROR(&@$, "Division by zero"); | ||||||
|  | 				$$ = 0; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	| integer_unary | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | integer_unary: | ||||||
|  | 	  integer_prim | ||||||
|  | 	| '-' integer_unary { $$ = -$2; } | ||||||
|  | 	| '~' integer_unary { $$ = ~$2; } | ||||||
|  | 	| '!' integer_unary { $$ = !$2; } | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | bytestring: | ||||||
|  | 	  /* empty */ | ||||||
|  | 		{ | ||||||
|  | 			$$ = empty_data; | ||||||
|  | 		} | ||||||
|  | 	| bytestring DT_BYTE | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_append_byte($1, $2); | ||||||
|  | 		} | ||||||
|  | 	| bytestring DT_LABEL | ||||||
|  | 		{ | ||||||
|  | 			$$ = data_add_marker($1, LABEL, $2); | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | subnodes: | ||||||
|  | 	  /* empty */ | ||||||
|  | 		{ | ||||||
|  | 			$$ = NULL; | ||||||
|  | 		} | ||||||
|  | 	| subnode subnodes | ||||||
|  | 		{ | ||||||
|  | 			$$ = chain_node($1, $2); | ||||||
|  | 		} | ||||||
|  | 	| subnode propdef | ||||||
|  | 		{ | ||||||
|  | 			ERROR(&@2, "Properties must precede subnodes"); | ||||||
|  | 			YYERROR; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | subnode: | ||||||
|  | 	  DT_PROPNODENAME nodedef | ||||||
|  | 		{ | ||||||
|  | 			$$ = name_node($2, $1); | ||||||
|  | 		} | ||||||
|  | 	| DT_DEL_NODE DT_PROPNODENAME ';' | ||||||
|  | 		{ | ||||||
|  | 			$$ = name_node(build_node_delete(), $2); | ||||||
|  | 		} | ||||||
|  | 	| DT_LABEL subnode | ||||||
|  | 		{ | ||||||
|  | 			add_label(&$2->labels, $1); | ||||||
|  | 			$$ = $2; | ||||||
|  | 		} | ||||||
|  | 	; | ||||||
|  |  | ||||||
|  | %% | ||||||
|  |  | ||||||
|  | void yyerror(char const *s) | ||||||
|  | { | ||||||
|  | 	ERROR(&yylloc, "%s", s); | ||||||
|  | } | ||||||
							
								
								
									
										366
									
								
								scripts/dtc/dtc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										366
									
								
								scripts/dtc/dtc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,366 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <sys/stat.h> | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  | #include "srcpos.h" | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Command line options | ||||||
|  |  */ | ||||||
|  | int quiet;		/* Level of quietness */ | ||||||
|  | int reservenum;		/* Number of memory reservation slots */ | ||||||
|  | int minsize;		/* Minimum blob size */ | ||||||
|  | int padsize;		/* Additional padding to blob */ | ||||||
|  | int alignsize;		/* Additional padding to blob accroding to the alignsize */ | ||||||
|  | int phandle_format = PHANDLE_BOTH;	/* Use linux,phandle or phandle properties */ | ||||||
|  | int generate_symbols;	/* enable symbols & fixup support */ | ||||||
|  | int generate_fixups;		/* suppress generation of fixups on symbol support */ | ||||||
|  | int auto_label_aliases;		/* auto generate labels -> aliases */ | ||||||
|  |  | ||||||
|  | static int is_power_of_2(int x) | ||||||
|  | { | ||||||
|  | 	return (x > 0) && ((x & (x - 1)) == 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fill_fullpaths(struct node *tree, const char *prefix) | ||||||
|  | { | ||||||
|  | 	struct node *child; | ||||||
|  | 	const char *unit; | ||||||
|  |  | ||||||
|  | 	tree->fullpath = join_path(prefix, tree->name); | ||||||
|  |  | ||||||
|  | 	unit = strchr(tree->name, '@'); | ||||||
|  | 	if (unit) | ||||||
|  | 		tree->basenamelen = unit - tree->name; | ||||||
|  | 	else | ||||||
|  | 		tree->basenamelen = strlen(tree->name); | ||||||
|  |  | ||||||
|  | 	for_each_child(tree, child) | ||||||
|  | 		fill_fullpaths(child, tree->fullpath); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Usage related data. */ | ||||||
|  | #define FDT_VERSION(version)	_FDT_VERSION(version) | ||||||
|  | #define _FDT_VERSION(version)	#version | ||||||
|  | static const char usage_synopsis[] = "dtc [options] <input file>"; | ||||||
|  | static const char usage_short_opts[] = "qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@Ahv"; | ||||||
|  | static struct option const usage_long_opts[] = { | ||||||
|  | 	{"quiet",            no_argument, NULL, 'q'}, | ||||||
|  | 	{"in-format",         a_argument, NULL, 'I'}, | ||||||
|  | 	{"out",               a_argument, NULL, 'o'}, | ||||||
|  | 	{"out-format",        a_argument, NULL, 'O'}, | ||||||
|  | 	{"out-version",       a_argument, NULL, 'V'}, | ||||||
|  | 	{"out-dependency",    a_argument, NULL, 'd'}, | ||||||
|  | 	{"reserve",           a_argument, NULL, 'R'}, | ||||||
|  | 	{"space",             a_argument, NULL, 'S'}, | ||||||
|  | 	{"pad",               a_argument, NULL, 'p'}, | ||||||
|  | 	{"align",             a_argument, NULL, 'a'}, | ||||||
|  | 	{"boot-cpu",          a_argument, NULL, 'b'}, | ||||||
|  | 	{"force",            no_argument, NULL, 'f'}, | ||||||
|  | 	{"include",           a_argument, NULL, 'i'}, | ||||||
|  | 	{"sort",             no_argument, NULL, 's'}, | ||||||
|  | 	{"phandle",           a_argument, NULL, 'H'}, | ||||||
|  | 	{"warning",           a_argument, NULL, 'W'}, | ||||||
|  | 	{"error",             a_argument, NULL, 'E'}, | ||||||
|  | 	{"symbols",	     no_argument, NULL, '@'}, | ||||||
|  | 	{"auto-alias",       no_argument, NULL, 'A'}, | ||||||
|  | 	{"help",             no_argument, NULL, 'h'}, | ||||||
|  | 	{"version",          no_argument, NULL, 'v'}, | ||||||
|  | 	{NULL,               no_argument, NULL, 0x0}, | ||||||
|  | }; | ||||||
|  | static const char * const usage_opts_help[] = { | ||||||
|  | 	"\n\tQuiet: -q suppress warnings, -qq errors, -qqq all", | ||||||
|  | 	"\n\tInput formats are:\n" | ||||||
|  | 	 "\t\tdts - device tree source text\n" | ||||||
|  | 	 "\t\tdtb - device tree blob\n" | ||||||
|  | 	 "\t\tfs  - /proc/device-tree style directory", | ||||||
|  | 	"\n\tOutput file", | ||||||
|  | 	"\n\tOutput formats are:\n" | ||||||
|  | 	 "\t\tdts - device tree source text\n" | ||||||
|  | 	 "\t\tdtb - device tree blob\n" | ||||||
|  | 	 "\t\tasm - assembler source", | ||||||
|  | 	"\n\tBlob version to produce, defaults to "FDT_VERSION(DEFAULT_FDT_VERSION)" (for dtb and asm output)", | ||||||
|  | 	"\n\tOutput dependency file", | ||||||
|  | 	"\n\tMake space for <number> reserve map entries (for dtb and asm output)", | ||||||
|  | 	"\n\tMake the blob at least <bytes> long (extra space)", | ||||||
|  | 	"\n\tAdd padding to the blob of <bytes> long (extra space)", | ||||||
|  | 	"\n\tMake the blob align to the <bytes> (extra space)", | ||||||
|  | 	"\n\tSet the physical boot cpu", | ||||||
|  | 	"\n\tTry to produce output even if the input tree has errors", | ||||||
|  | 	"\n\tAdd a path to search for include files", | ||||||
|  | 	"\n\tSort nodes and properties before outputting (useful for comparing trees)", | ||||||
|  | 	"\n\tValid phandle formats are:\n" | ||||||
|  | 	 "\t\tlegacy - \"linux,phandle\" properties only\n" | ||||||
|  | 	 "\t\tepapr  - \"phandle\" properties only\n" | ||||||
|  | 	 "\t\tboth   - Both \"linux,phandle\" and \"phandle\" properties", | ||||||
|  | 	"\n\tEnable/disable warnings (prefix with \"no-\")", | ||||||
|  | 	"\n\tEnable/disable errors (prefix with \"no-\")", | ||||||
|  | 	"\n\tEnable generation of symbols", | ||||||
|  | 	"\n\tEnable auto-alias of labels", | ||||||
|  | 	"\n\tPrint this help and exit", | ||||||
|  | 	"\n\tPrint version and exit", | ||||||
|  | 	NULL, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const char *guess_type_by_name(const char *fname, const char *fallback) | ||||||
|  | { | ||||||
|  | 	const char *s; | ||||||
|  |  | ||||||
|  | 	s = strrchr(fname, '.'); | ||||||
|  | 	if (s == NULL) | ||||||
|  | 		return fallback; | ||||||
|  | 	if (!strcasecmp(s, ".dts")) | ||||||
|  | 		return "dts"; | ||||||
|  | 	if (!strcasecmp(s, ".dtb")) | ||||||
|  | 		return "dtb"; | ||||||
|  | 	return fallback; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const char *guess_input_format(const char *fname, const char *fallback) | ||||||
|  | { | ||||||
|  | 	struct stat statbuf; | ||||||
|  | 	uint32_t magic; | ||||||
|  | 	FILE *f; | ||||||
|  |  | ||||||
|  | 	if (stat(fname, &statbuf) != 0) | ||||||
|  | 		return fallback; | ||||||
|  |  | ||||||
|  | 	if (S_ISDIR(statbuf.st_mode)) | ||||||
|  | 		return "fs"; | ||||||
|  |  | ||||||
|  | 	if (!S_ISREG(statbuf.st_mode)) | ||||||
|  | 		return fallback; | ||||||
|  |  | ||||||
|  | 	f = fopen(fname, "r"); | ||||||
|  | 	if (f == NULL) | ||||||
|  | 		return fallback; | ||||||
|  | 	if (fread(&magic, 4, 1, f) != 1) { | ||||||
|  | 		fclose(f); | ||||||
|  | 		return fallback; | ||||||
|  | 	} | ||||||
|  | 	fclose(f); | ||||||
|  |  | ||||||
|  | 	magic = fdt32_to_cpu(magic); | ||||||
|  | 	if (magic == FDT_MAGIC) | ||||||
|  | 		return "dtb"; | ||||||
|  |  | ||||||
|  | 	return guess_type_by_name(fname, fallback); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  | 	struct dt_info *dti; | ||||||
|  | 	const char *inform = NULL; | ||||||
|  | 	const char *outform = NULL; | ||||||
|  | 	const char *outname = "-"; | ||||||
|  | 	const char *depname = NULL; | ||||||
|  | 	bool force = false, sort = false; | ||||||
|  | 	const char *arg; | ||||||
|  | 	int opt; | ||||||
|  | 	FILE *outf = NULL; | ||||||
|  | 	int outversion = DEFAULT_FDT_VERSION; | ||||||
|  | 	long long cmdline_boot_cpuid = -1; | ||||||
|  |  | ||||||
|  | 	quiet      = 0; | ||||||
|  | 	reservenum = 0; | ||||||
|  | 	minsize    = 0; | ||||||
|  | 	padsize    = 0; | ||||||
|  | 	alignsize  = 0; | ||||||
|  |  | ||||||
|  | 	while ((opt = util_getopt_long()) != EOF) { | ||||||
|  | 		switch (opt) { | ||||||
|  | 		case 'I': | ||||||
|  | 			inform = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'O': | ||||||
|  | 			outform = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'o': | ||||||
|  | 			outname = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'V': | ||||||
|  | 			outversion = strtol(optarg, NULL, 0); | ||||||
|  | 			break; | ||||||
|  | 		case 'd': | ||||||
|  | 			depname = optarg; | ||||||
|  | 			break; | ||||||
|  | 		case 'R': | ||||||
|  | 			reservenum = strtol(optarg, NULL, 0); | ||||||
|  | 			break; | ||||||
|  | 		case 'S': | ||||||
|  | 			minsize = strtol(optarg, NULL, 0); | ||||||
|  | 			break; | ||||||
|  | 		case 'p': | ||||||
|  | 			padsize = strtol(optarg, NULL, 0); | ||||||
|  | 			break; | ||||||
|  | 		case 'a': | ||||||
|  | 			alignsize = strtol(optarg, NULL, 0); | ||||||
|  | 			if (!is_power_of_2(alignsize)) | ||||||
|  | 				die("Invalid argument \"%d\" to -a option\n", | ||||||
|  | 				    alignsize); | ||||||
|  | 			break; | ||||||
|  | 		case 'f': | ||||||
|  | 			force = true; | ||||||
|  | 			break; | ||||||
|  | 		case 'q': | ||||||
|  | 			quiet++; | ||||||
|  | 			break; | ||||||
|  | 		case 'b': | ||||||
|  | 			cmdline_boot_cpuid = strtoll(optarg, NULL, 0); | ||||||
|  | 			break; | ||||||
|  | 		case 'i': | ||||||
|  | 			srcfile_add_search_path(optarg); | ||||||
|  | 			break; | ||||||
|  | 		case 'v': | ||||||
|  | 			util_version(); | ||||||
|  | 		case 'H': | ||||||
|  | 			if (streq(optarg, "legacy")) | ||||||
|  | 				phandle_format = PHANDLE_LEGACY; | ||||||
|  | 			else if (streq(optarg, "epapr")) | ||||||
|  | 				phandle_format = PHANDLE_EPAPR; | ||||||
|  | 			else if (streq(optarg, "both")) | ||||||
|  | 				phandle_format = PHANDLE_BOTH; | ||||||
|  | 			else | ||||||
|  | 				die("Invalid argument \"%s\" to -H option\n", | ||||||
|  | 				    optarg); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case 's': | ||||||
|  | 			sort = true; | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case 'W': | ||||||
|  | 			parse_checks_option(true, false, optarg); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case 'E': | ||||||
|  | 			parse_checks_option(false, true, optarg); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case '@': | ||||||
|  | 			generate_symbols = 1; | ||||||
|  | 			break; | ||||||
|  | 		case 'A': | ||||||
|  | 			auto_label_aliases = 1; | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case 'h': | ||||||
|  | 			usage(NULL); | ||||||
|  | 		default: | ||||||
|  | 			usage("unknown option"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (argc > (optind+1)) | ||||||
|  | 		usage("missing files"); | ||||||
|  | 	else if (argc < (optind+1)) | ||||||
|  | 		arg = "-"; | ||||||
|  | 	else | ||||||
|  | 		arg = argv[optind]; | ||||||
|  |  | ||||||
|  | 	/* minsize and padsize are mutually exclusive */ | ||||||
|  | 	if (minsize && padsize) | ||||||
|  | 		die("Can't set both -p and -S\n"); | ||||||
|  |  | ||||||
|  | 	if (depname) { | ||||||
|  | 		depfile = fopen(depname, "w"); | ||||||
|  | 		if (!depfile) | ||||||
|  | 			die("Couldn't open dependency file %s: %s\n", depname, | ||||||
|  | 			    strerror(errno)); | ||||||
|  | 		fprintf(depfile, "%s:", outname); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (inform == NULL) | ||||||
|  | 		inform = guess_input_format(arg, "dts"); | ||||||
|  | 	if (outform == NULL) { | ||||||
|  | 		outform = guess_type_by_name(outname, NULL); | ||||||
|  | 		if (outform == NULL) { | ||||||
|  | 			if (streq(inform, "dts")) | ||||||
|  | 				outform = "dtb"; | ||||||
|  | 			else | ||||||
|  | 				outform = "dts"; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (streq(inform, "dts")) | ||||||
|  | 		dti = dt_from_source(arg); | ||||||
|  | 	else if (streq(inform, "fs")) | ||||||
|  | 		dti = dt_from_fs(arg); | ||||||
|  | 	else if(streq(inform, "dtb")) | ||||||
|  | 		dti = dt_from_blob(arg); | ||||||
|  | 	else | ||||||
|  | 		die("Unknown input format \"%s\"\n", inform); | ||||||
|  |  | ||||||
|  | 	dti->outname = outname; | ||||||
|  |  | ||||||
|  | 	if (depfile) { | ||||||
|  | 		fputc('\n', depfile); | ||||||
|  | 		fclose(depfile); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (cmdline_boot_cpuid != -1) | ||||||
|  | 		dti->boot_cpuid_phys = cmdline_boot_cpuid; | ||||||
|  |  | ||||||
|  | 	fill_fullpaths(dti->dt, ""); | ||||||
|  | 	process_checks(force, dti); | ||||||
|  |  | ||||||
|  | 	/* on a plugin, generate by default */ | ||||||
|  | 	if (dti->dtsflags & DTSF_PLUGIN) { | ||||||
|  | 		generate_fixups = 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (auto_label_aliases) | ||||||
|  | 		generate_label_tree(dti, "aliases", false); | ||||||
|  |  | ||||||
|  | 	if (generate_symbols) | ||||||
|  | 		generate_label_tree(dti, "__symbols__", true); | ||||||
|  |  | ||||||
|  | 	if (generate_fixups) { | ||||||
|  | 		generate_fixups_tree(dti, "__fixups__"); | ||||||
|  | 		generate_local_fixups_tree(dti, "__local_fixups__"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (sort) | ||||||
|  | 		sort_tree(dti); | ||||||
|  |  | ||||||
|  | 	if (streq(outname, "-")) { | ||||||
|  | 		outf = stdout; | ||||||
|  | 	} else { | ||||||
|  | 		outf = fopen(outname, "wb"); | ||||||
|  | 		if (! outf) | ||||||
|  | 			die("Couldn't open output file %s: %s\n", | ||||||
|  | 			    outname, strerror(errno)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (streq(outform, "dts")) { | ||||||
|  | 		dt_to_source(outf, dti); | ||||||
|  | 	} else if (streq(outform, "dtb")) { | ||||||
|  | 		dt_to_blob(outf, dti, outversion); | ||||||
|  | 	} else if (streq(outform, "asm")) { | ||||||
|  | 		dt_to_asm(outf, dti, outversion); | ||||||
|  | 	} else if (streq(outform, "null")) { | ||||||
|  | 		/* do nothing */ | ||||||
|  | 	} else { | ||||||
|  | 		die("Unknown output format \"%s\"\n", outform); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	exit(0); | ||||||
|  | } | ||||||
							
								
								
									
										285
									
								
								scripts/dtc/dtc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								scripts/dtc/dtc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | |||||||
|  | #ifndef _DTC_H | ||||||
|  | #define _DTC_H | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
|  | #include <libfdt_env.h> | ||||||
|  | #include <fdt.h> | ||||||
|  |  | ||||||
|  | #include "util.h" | ||||||
|  |  | ||||||
|  | #ifdef DEBUG | ||||||
|  | #define debug(...)	printf(__VA_ARGS__) | ||||||
|  | #else | ||||||
|  | #define debug(...) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define DEFAULT_FDT_VERSION	17 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Command line options | ||||||
|  |  */ | ||||||
|  | extern int quiet;		/* Level of quietness */ | ||||||
|  | extern int reservenum;		/* Number of memory reservation slots */ | ||||||
|  | extern int minsize;		/* Minimum blob size */ | ||||||
|  | extern int padsize;		/* Additional padding to blob */ | ||||||
|  | extern int alignsize;		/* Additional padding to blob accroding to the alignsize */ | ||||||
|  | extern int phandle_format;	/* Use linux,phandle or phandle properties */ | ||||||
|  | extern int generate_symbols;	/* generate symbols for nodes with labels */ | ||||||
|  | extern int generate_fixups;	/* generate fixups */ | ||||||
|  | extern int auto_label_aliases;	/* auto generate labels -> aliases */ | ||||||
|  |  | ||||||
|  | #define PHANDLE_LEGACY	0x1 | ||||||
|  | #define PHANDLE_EPAPR	0x2 | ||||||
|  | #define PHANDLE_BOTH	0x3 | ||||||
|  |  | ||||||
|  | typedef uint32_t cell_t; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define streq(a, b)	(strcmp((a), (b)) == 0) | ||||||
|  | #define strneq(a, b, n)	(strncmp((a), (b), (n)) == 0) | ||||||
|  |  | ||||||
|  | #define ALIGN(x, a)	(((x) + (a) - 1) & ~((a) - 1)) | ||||||
|  |  | ||||||
|  | /* Data blobs */ | ||||||
|  | enum markertype { | ||||||
|  | 	REF_PHANDLE, | ||||||
|  | 	REF_PATH, | ||||||
|  | 	LABEL, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct  marker { | ||||||
|  | 	enum markertype type; | ||||||
|  | 	int offset; | ||||||
|  | 	char *ref; | ||||||
|  | 	struct marker *next; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct data { | ||||||
|  | 	int len; | ||||||
|  | 	char *val; | ||||||
|  | 	struct marker *markers; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define empty_data ((struct data){ 0 /* all .members = 0 or NULL */ }) | ||||||
|  |  | ||||||
|  | #define for_each_marker(m) \ | ||||||
|  | 	for (; (m); (m) = (m)->next) | ||||||
|  | #define for_each_marker_of_type(m, t) \ | ||||||
|  | 	for_each_marker(m) \ | ||||||
|  | 		if ((m)->type == (t)) | ||||||
|  |  | ||||||
|  | void data_free(struct data d); | ||||||
|  |  | ||||||
|  | struct data data_grow_for(struct data d, int xlen); | ||||||
|  |  | ||||||
|  | struct data data_copy_mem(const char *mem, int len); | ||||||
|  | struct data data_copy_escape_string(const char *s, int len); | ||||||
|  | struct data data_copy_file(FILE *f, size_t len); | ||||||
|  |  | ||||||
|  | struct data data_append_data(struct data d, const void *p, int len); | ||||||
|  | struct data data_insert_at_marker(struct data d, struct marker *m, | ||||||
|  | 				  const void *p, int len); | ||||||
|  | struct data data_merge(struct data d1, struct data d2); | ||||||
|  | struct data data_append_cell(struct data d, cell_t word); | ||||||
|  | struct data data_append_integer(struct data d, uint64_t word, int bits); | ||||||
|  | struct data data_append_re(struct data d, const struct fdt_reserve_entry *re); | ||||||
|  | struct data data_append_addr(struct data d, uint64_t addr); | ||||||
|  | struct data data_append_byte(struct data d, uint8_t byte); | ||||||
|  | struct data data_append_zeroes(struct data d, int len); | ||||||
|  | struct data data_append_align(struct data d, int align); | ||||||
|  |  | ||||||
|  | struct data data_add_marker(struct data d, enum markertype type, char *ref); | ||||||
|  |  | ||||||
|  | bool data_is_one_string(struct data d); | ||||||
|  |  | ||||||
|  | /* DT constraints */ | ||||||
|  |  | ||||||
|  | #define MAX_PROPNAME_LEN	31 | ||||||
|  | #define MAX_NODENAME_LEN	31 | ||||||
|  |  | ||||||
|  | /* Live trees */ | ||||||
|  | struct label { | ||||||
|  | 	bool deleted; | ||||||
|  | 	char *label; | ||||||
|  | 	struct label *next; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct property { | ||||||
|  | 	bool deleted; | ||||||
|  | 	char *name; | ||||||
|  | 	struct data val; | ||||||
|  |  | ||||||
|  | 	struct property *next; | ||||||
|  |  | ||||||
|  | 	struct label *labels; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct node { | ||||||
|  | 	bool deleted; | ||||||
|  | 	char *name; | ||||||
|  | 	struct property *proplist; | ||||||
|  | 	struct node *children; | ||||||
|  |  | ||||||
|  | 	struct node *parent; | ||||||
|  | 	struct node *next_sibling; | ||||||
|  |  | ||||||
|  | 	char *fullpath; | ||||||
|  | 	int basenamelen; | ||||||
|  |  | ||||||
|  | 	cell_t phandle; | ||||||
|  | 	int addr_cells, size_cells; | ||||||
|  |  | ||||||
|  | 	struct label *labels; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define for_each_label_withdel(l0, l) \ | ||||||
|  | 	for ((l) = (l0); (l); (l) = (l)->next) | ||||||
|  |  | ||||||
|  | #define for_each_label(l0, l) \ | ||||||
|  | 	for_each_label_withdel(l0, l) \ | ||||||
|  | 		if (!(l)->deleted) | ||||||
|  |  | ||||||
|  | #define for_each_property_withdel(n, p) \ | ||||||
|  | 	for ((p) = (n)->proplist; (p); (p) = (p)->next) | ||||||
|  |  | ||||||
|  | #define for_each_property(n, p) \ | ||||||
|  | 	for_each_property_withdel(n, p) \ | ||||||
|  | 		if (!(p)->deleted) | ||||||
|  |  | ||||||
|  | #define for_each_child_withdel(n, c) \ | ||||||
|  | 	for ((c) = (n)->children; (c); (c) = (c)->next_sibling) | ||||||
|  |  | ||||||
|  | #define for_each_child(n, c) \ | ||||||
|  | 	for_each_child_withdel(n, c) \ | ||||||
|  | 		if (!(c)->deleted) | ||||||
|  |  | ||||||
|  | void add_label(struct label **labels, char *label); | ||||||
|  | void delete_labels(struct label **labels); | ||||||
|  |  | ||||||
|  | struct property *build_property(char *name, struct data val); | ||||||
|  | struct property *build_property_delete(char *name); | ||||||
|  | struct property *chain_property(struct property *first, struct property *list); | ||||||
|  | struct property *reverse_properties(struct property *first); | ||||||
|  |  | ||||||
|  | struct node *build_node(struct property *proplist, struct node *children); | ||||||
|  | struct node *build_node_delete(void); | ||||||
|  | struct node *name_node(struct node *node, char *name); | ||||||
|  | struct node *chain_node(struct node *first, struct node *list); | ||||||
|  | struct node *merge_nodes(struct node *old_node, struct node *new_node); | ||||||
|  |  | ||||||
|  | void add_property(struct node *node, struct property *prop); | ||||||
|  | void delete_property_by_name(struct node *node, char *name); | ||||||
|  | void delete_property(struct property *prop); | ||||||
|  | void add_child(struct node *parent, struct node *child); | ||||||
|  | void delete_node_by_name(struct node *parent, char *name); | ||||||
|  | void delete_node(struct node *node); | ||||||
|  | void append_to_property(struct node *node, | ||||||
|  | 			char *name, const void *data, int len); | ||||||
|  |  | ||||||
|  | const char *get_unitname(struct node *node); | ||||||
|  | struct property *get_property(struct node *node, const char *propname); | ||||||
|  | cell_t propval_cell(struct property *prop); | ||||||
|  | struct property *get_property_by_label(struct node *tree, const char *label, | ||||||
|  | 				       struct node **node); | ||||||
|  | struct marker *get_marker_label(struct node *tree, const char *label, | ||||||
|  | 				struct node **node, struct property **prop); | ||||||
|  | struct node *get_subnode(struct node *node, const char *nodename); | ||||||
|  | struct node *get_node_by_path(struct node *tree, const char *path); | ||||||
|  | struct node *get_node_by_label(struct node *tree, const char *label); | ||||||
|  | struct node *get_node_by_phandle(struct node *tree, cell_t phandle); | ||||||
|  | struct node *get_node_by_ref(struct node *tree, const char *ref); | ||||||
|  | cell_t get_node_phandle(struct node *root, struct node *node); | ||||||
|  |  | ||||||
|  | uint32_t guess_boot_cpuid(struct node *tree); | ||||||
|  |  | ||||||
|  | /* Boot info (tree plus memreserve information */ | ||||||
|  |  | ||||||
|  | struct reserve_info { | ||||||
|  | 	struct fdt_reserve_entry re; | ||||||
|  |  | ||||||
|  | 	struct reserve_info *next; | ||||||
|  |  | ||||||
|  | 	struct label *labels; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct reserve_info *build_reserve_entry(uint64_t start, uint64_t len); | ||||||
|  | struct reserve_info *chain_reserve_entry(struct reserve_info *first, | ||||||
|  | 					 struct reserve_info *list); | ||||||
|  | struct reserve_info *add_reserve_entry(struct reserve_info *list, | ||||||
|  | 				       struct reserve_info *new); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct dt_info { | ||||||
|  | 	unsigned int dtsflags; | ||||||
|  | 	struct reserve_info *reservelist; | ||||||
|  | 	uint32_t boot_cpuid_phys; | ||||||
|  | 	struct node *dt;		/* the device tree */ | ||||||
|  | 	const char *outname;		/* filename being written to, "-" for stdout */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* DTS version flags definitions */ | ||||||
|  | #define DTSF_V1		0x0001	/* /dts-v1/ */ | ||||||
|  | #define DTSF_PLUGIN	0x0002	/* /plugin/ */ | ||||||
|  |  | ||||||
|  | struct dt_info *build_dt_info(unsigned int dtsflags, | ||||||
|  | 			      struct reserve_info *reservelist, | ||||||
|  | 			      struct node *tree, uint32_t boot_cpuid_phys); | ||||||
|  | void sort_tree(struct dt_info *dti); | ||||||
|  | void generate_label_tree(struct dt_info *dti, char *name, bool allocph); | ||||||
|  | void generate_fixups_tree(struct dt_info *dti, char *name); | ||||||
|  | void generate_local_fixups_tree(struct dt_info *dti, char *name); | ||||||
|  |  | ||||||
|  | /* Checks */ | ||||||
|  |  | ||||||
|  | void parse_checks_option(bool warn, bool error, const char *arg); | ||||||
|  | void process_checks(bool force, struct dt_info *dti); | ||||||
|  |  | ||||||
|  | /* Flattened trees */ | ||||||
|  |  | ||||||
|  | void dt_to_blob(FILE *f, struct dt_info *dti, int version); | ||||||
|  | void dt_to_asm(FILE *f, struct dt_info *dti, int version); | ||||||
|  |  | ||||||
|  | struct dt_info *dt_from_blob(const char *fname); | ||||||
|  |  | ||||||
|  | /* Tree source */ | ||||||
|  |  | ||||||
|  | void dt_to_source(FILE *f, struct dt_info *dti); | ||||||
|  | struct dt_info *dt_from_source(const char *f); | ||||||
|  |  | ||||||
|  | /* FS trees */ | ||||||
|  |  | ||||||
|  | struct dt_info *dt_from_fs(const char *dirname); | ||||||
|  |  | ||||||
|  | #endif /* _DTC_H */ | ||||||
							
								
								
									
										946
									
								
								scripts/dtc/flattree.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										946
									
								
								scripts/dtc/flattree.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,946 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  | #include "srcpos.h" | ||||||
|  |  | ||||||
|  | #define FTF_FULLPATH	0x1 | ||||||
|  | #define FTF_VARALIGN	0x2 | ||||||
|  | #define FTF_NAMEPROPS	0x4 | ||||||
|  | #define FTF_BOOTCPUID	0x8 | ||||||
|  | #define FTF_STRTABSIZE	0x10 | ||||||
|  | #define FTF_STRUCTSIZE	0x20 | ||||||
|  | #define FTF_NOPS	0x40 | ||||||
|  |  | ||||||
|  | static struct version_info { | ||||||
|  | 	int version; | ||||||
|  | 	int last_comp_version; | ||||||
|  | 	int hdr_size; | ||||||
|  | 	int flags; | ||||||
|  | } version_table[] = { | ||||||
|  | 	{1, 1, FDT_V1_SIZE, | ||||||
|  | 	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS}, | ||||||
|  | 	{2, 1, FDT_V2_SIZE, | ||||||
|  | 	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID}, | ||||||
|  | 	{3, 1, FDT_V3_SIZE, | ||||||
|  | 	 FTF_FULLPATH|FTF_VARALIGN|FTF_NAMEPROPS|FTF_BOOTCPUID|FTF_STRTABSIZE}, | ||||||
|  | 	{16, 16, FDT_V3_SIZE, | ||||||
|  | 	 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_NOPS}, | ||||||
|  | 	{17, 16, FDT_V17_SIZE, | ||||||
|  | 	 FTF_BOOTCPUID|FTF_STRTABSIZE|FTF_STRUCTSIZE|FTF_NOPS}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct emitter { | ||||||
|  | 	void (*cell)(void *, cell_t); | ||||||
|  | 	void (*string)(void *, char *, int); | ||||||
|  | 	void (*align)(void *, int); | ||||||
|  | 	void (*data)(void *, struct data); | ||||||
|  | 	void (*beginnode)(void *, struct label *labels); | ||||||
|  | 	void (*endnode)(void *, struct label *labels); | ||||||
|  | 	void (*property)(void *, struct label *labels); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void bin_emit_cell(void *e, cell_t val) | ||||||
|  | { | ||||||
|  | 	struct data *dtbuf = e; | ||||||
|  |  | ||||||
|  | 	*dtbuf = data_append_cell(*dtbuf, val); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bin_emit_string(void *e, char *str, int len) | ||||||
|  | { | ||||||
|  | 	struct data *dtbuf = e; | ||||||
|  |  | ||||||
|  | 	if (len == 0) | ||||||
|  | 		len = strlen(str); | ||||||
|  |  | ||||||
|  | 	*dtbuf = data_append_data(*dtbuf, str, len); | ||||||
|  | 	*dtbuf = data_append_byte(*dtbuf, '\0'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bin_emit_align(void *e, int a) | ||||||
|  | { | ||||||
|  | 	struct data *dtbuf = e; | ||||||
|  |  | ||||||
|  | 	*dtbuf = data_append_align(*dtbuf, a); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bin_emit_data(void *e, struct data d) | ||||||
|  | { | ||||||
|  | 	struct data *dtbuf = e; | ||||||
|  |  | ||||||
|  | 	*dtbuf = data_append_data(*dtbuf, d.val, d.len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bin_emit_beginnode(void *e, struct label *labels) | ||||||
|  | { | ||||||
|  | 	bin_emit_cell(e, FDT_BEGIN_NODE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bin_emit_endnode(void *e, struct label *labels) | ||||||
|  | { | ||||||
|  | 	bin_emit_cell(e, FDT_END_NODE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void bin_emit_property(void *e, struct label *labels) | ||||||
|  | { | ||||||
|  | 	bin_emit_cell(e, FDT_PROP); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct emitter bin_emitter = { | ||||||
|  | 	.cell = bin_emit_cell, | ||||||
|  | 	.string = bin_emit_string, | ||||||
|  | 	.align = bin_emit_align, | ||||||
|  | 	.data = bin_emit_data, | ||||||
|  | 	.beginnode = bin_emit_beginnode, | ||||||
|  | 	.endnode = bin_emit_endnode, | ||||||
|  | 	.property = bin_emit_property, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void emit_label(FILE *f, const char *prefix, const char *label) | ||||||
|  | { | ||||||
|  | 	fprintf(f, "\t.globl\t%s_%s\n", prefix, label); | ||||||
|  | 	fprintf(f, "%s_%s:\n", prefix, label); | ||||||
|  | 	fprintf(f, "_%s_%s:\n", prefix, label); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void emit_offset_label(FILE *f, const char *label, int offset) | ||||||
|  | { | ||||||
|  | 	fprintf(f, "\t.globl\t%s\n", label); | ||||||
|  | 	fprintf(f, "%s\t= . + %d\n", label, offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define ASM_EMIT_BELONG(f, fmt, ...) \ | ||||||
|  | 	{ \ | ||||||
|  | 		fprintf((f), "\t.byte\t((" fmt ") >> 24) & 0xff\n", __VA_ARGS__); \ | ||||||
|  | 		fprintf((f), "\t.byte\t((" fmt ") >> 16) & 0xff\n", __VA_ARGS__); \ | ||||||
|  | 		fprintf((f), "\t.byte\t((" fmt ") >> 8) & 0xff\n", __VA_ARGS__); \ | ||||||
|  | 		fprintf((f), "\t.byte\t(" fmt ") & 0xff\n", __VA_ARGS__); \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | static void asm_emit_cell(void *e, cell_t val) | ||||||
|  | { | ||||||
|  | 	FILE *f = e; | ||||||
|  |  | ||||||
|  | 	fprintf(f, "\t.byte 0x%02x; .byte 0x%02x; .byte 0x%02x; .byte 0x%02x\n", | ||||||
|  | 		(val >> 24) & 0xff, (val >> 16) & 0xff, | ||||||
|  | 		(val >> 8) & 0xff, val & 0xff); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void asm_emit_string(void *e, char *str, int len) | ||||||
|  | { | ||||||
|  | 	FILE *f = e; | ||||||
|  | 	char c = 0; | ||||||
|  |  | ||||||
|  | 	if (len != 0) { | ||||||
|  | 		/* XXX: ewww */ | ||||||
|  | 		c = str[len]; | ||||||
|  | 		str[len] = '\0'; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fprintf(f, "\t.string\t\"%s\"\n", str); | ||||||
|  |  | ||||||
|  | 	if (len != 0) { | ||||||
|  | 		str[len] = c; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void asm_emit_align(void *e, int a) | ||||||
|  | { | ||||||
|  | 	FILE *f = e; | ||||||
|  |  | ||||||
|  | 	fprintf(f, "\t.balign\t%d, 0\n", a); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void asm_emit_data(void *e, struct data d) | ||||||
|  | { | ||||||
|  | 	FILE *f = e; | ||||||
|  | 	int off = 0; | ||||||
|  | 	struct marker *m = d.markers; | ||||||
|  |  | ||||||
|  | 	for_each_marker_of_type(m, LABEL) | ||||||
|  | 		emit_offset_label(f, m->ref, m->offset); | ||||||
|  |  | ||||||
|  | 	while ((d.len - off) >= sizeof(uint32_t)) { | ||||||
|  | 		asm_emit_cell(e, fdt32_to_cpu(*((uint32_t *)(d.val+off)))); | ||||||
|  | 		off += sizeof(uint32_t); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while ((d.len - off) >= 1) { | ||||||
|  | 		fprintf(f, "\t.byte\t0x%hhx\n", d.val[off]); | ||||||
|  | 		off += 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	assert(off == d.len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void asm_emit_beginnode(void *e, struct label *labels) | ||||||
|  | { | ||||||
|  | 	FILE *f = e; | ||||||
|  | 	struct label *l; | ||||||
|  |  | ||||||
|  | 	for_each_label(labels, l) { | ||||||
|  | 		fprintf(f, "\t.globl\t%s\n", l->label); | ||||||
|  | 		fprintf(f, "%s:\n", l->label); | ||||||
|  | 	} | ||||||
|  | 	fprintf(f, "\t/* FDT_BEGIN_NODE */\n"); | ||||||
|  | 	asm_emit_cell(e, FDT_BEGIN_NODE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void asm_emit_endnode(void *e, struct label *labels) | ||||||
|  | { | ||||||
|  | 	FILE *f = e; | ||||||
|  | 	struct label *l; | ||||||
|  |  | ||||||
|  | 	fprintf(f, "\t/* FDT_END_NODE */\n"); | ||||||
|  | 	asm_emit_cell(e, FDT_END_NODE); | ||||||
|  | 	for_each_label(labels, l) { | ||||||
|  | 		fprintf(f, "\t.globl\t%s_end\n", l->label); | ||||||
|  | 		fprintf(f, "%s_end:\n", l->label); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void asm_emit_property(void *e, struct label *labels) | ||||||
|  | { | ||||||
|  | 	FILE *f = e; | ||||||
|  | 	struct label *l; | ||||||
|  |  | ||||||
|  | 	for_each_label(labels, l) { | ||||||
|  | 		fprintf(f, "\t.globl\t%s\n", l->label); | ||||||
|  | 		fprintf(f, "%s:\n", l->label); | ||||||
|  | 	} | ||||||
|  | 	fprintf(f, "\t/* FDT_PROP */\n"); | ||||||
|  | 	asm_emit_cell(e, FDT_PROP); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct emitter asm_emitter = { | ||||||
|  | 	.cell = asm_emit_cell, | ||||||
|  | 	.string = asm_emit_string, | ||||||
|  | 	.align = asm_emit_align, | ||||||
|  | 	.data = asm_emit_data, | ||||||
|  | 	.beginnode = asm_emit_beginnode, | ||||||
|  | 	.endnode = asm_emit_endnode, | ||||||
|  | 	.property = asm_emit_property, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int stringtable_insert(struct data *d, const char *str) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	/* FIXME: do this more efficiently? */ | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < d->len; i++) { | ||||||
|  | 		if (streq(str, d->val + i)) | ||||||
|  | 			return i; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	*d = data_append_data(*d, str, strlen(str)+1); | ||||||
|  | 	return i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void flatten_tree(struct node *tree, struct emitter *emit, | ||||||
|  | 			 void *etarget, struct data *strbuf, | ||||||
|  | 			 struct version_info *vi) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct node *child; | ||||||
|  | 	bool seen_name_prop = false; | ||||||
|  |  | ||||||
|  | 	if (tree->deleted) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	emit->beginnode(etarget, tree->labels); | ||||||
|  |  | ||||||
|  | 	if (vi->flags & FTF_FULLPATH) | ||||||
|  | 		emit->string(etarget, tree->fullpath, 0); | ||||||
|  | 	else | ||||||
|  | 		emit->string(etarget, tree->name, 0); | ||||||
|  |  | ||||||
|  | 	emit->align(etarget, sizeof(cell_t)); | ||||||
|  |  | ||||||
|  | 	for_each_property(tree, prop) { | ||||||
|  | 		int nameoff; | ||||||
|  |  | ||||||
|  | 		if (streq(prop->name, "name")) | ||||||
|  | 			seen_name_prop = true; | ||||||
|  |  | ||||||
|  | 		nameoff = stringtable_insert(strbuf, prop->name); | ||||||
|  |  | ||||||
|  | 		emit->property(etarget, prop->labels); | ||||||
|  | 		emit->cell(etarget, prop->val.len); | ||||||
|  | 		emit->cell(etarget, nameoff); | ||||||
|  |  | ||||||
|  | 		if ((vi->flags & FTF_VARALIGN) && (prop->val.len >= 8)) | ||||||
|  | 			emit->align(etarget, 8); | ||||||
|  |  | ||||||
|  | 		emit->data(etarget, prop->val); | ||||||
|  | 		emit->align(etarget, sizeof(cell_t)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ((vi->flags & FTF_NAMEPROPS) && !seen_name_prop) { | ||||||
|  | 		emit->property(etarget, NULL); | ||||||
|  | 		emit->cell(etarget, tree->basenamelen+1); | ||||||
|  | 		emit->cell(etarget, stringtable_insert(strbuf, "name")); | ||||||
|  |  | ||||||
|  | 		if ((vi->flags & FTF_VARALIGN) && ((tree->basenamelen+1) >= 8)) | ||||||
|  | 			emit->align(etarget, 8); | ||||||
|  |  | ||||||
|  | 		emit->string(etarget, tree->name, tree->basenamelen); | ||||||
|  | 		emit->align(etarget, sizeof(cell_t)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(tree, child) { | ||||||
|  | 		flatten_tree(child, emit, etarget, strbuf, vi); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	emit->endnode(etarget, tree->labels); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct data flatten_reserve_list(struct reserve_info *reservelist, | ||||||
|  | 				 struct version_info *vi) | ||||||
|  | { | ||||||
|  | 	struct reserve_info *re; | ||||||
|  | 	struct data d = empty_data; | ||||||
|  | 	static struct fdt_reserve_entry null_re = {0,0}; | ||||||
|  | 	int    j; | ||||||
|  |  | ||||||
|  | 	for (re = reservelist; re; re = re->next) { | ||||||
|  | 		d = data_append_re(d, &re->re); | ||||||
|  | 	} | ||||||
|  | 	/* | ||||||
|  | 	 * Add additional reserved slots if the user asked for them. | ||||||
|  | 	 */ | ||||||
|  | 	for (j = 0; j < reservenum; j++) { | ||||||
|  | 		d = data_append_re(d, &null_re); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void make_fdt_header(struct fdt_header *fdt, | ||||||
|  | 			    struct version_info *vi, | ||||||
|  | 			    int reservesize, int dtsize, int strsize, | ||||||
|  | 			    int boot_cpuid_phys) | ||||||
|  | { | ||||||
|  | 	int reserve_off; | ||||||
|  |  | ||||||
|  | 	reservesize += sizeof(struct fdt_reserve_entry); | ||||||
|  |  | ||||||
|  | 	memset(fdt, 0xff, sizeof(*fdt)); | ||||||
|  |  | ||||||
|  | 	fdt->magic = cpu_to_fdt32(FDT_MAGIC); | ||||||
|  | 	fdt->version = cpu_to_fdt32(vi->version); | ||||||
|  | 	fdt->last_comp_version = cpu_to_fdt32(vi->last_comp_version); | ||||||
|  |  | ||||||
|  | 	/* Reserve map should be doubleword aligned */ | ||||||
|  | 	reserve_off = ALIGN(vi->hdr_size, 8); | ||||||
|  |  | ||||||
|  | 	fdt->off_mem_rsvmap = cpu_to_fdt32(reserve_off); | ||||||
|  | 	fdt->off_dt_struct = cpu_to_fdt32(reserve_off + reservesize); | ||||||
|  | 	fdt->off_dt_strings = cpu_to_fdt32(reserve_off + reservesize | ||||||
|  | 					  + dtsize); | ||||||
|  | 	fdt->totalsize = cpu_to_fdt32(reserve_off + reservesize + dtsize + strsize); | ||||||
|  |  | ||||||
|  | 	if (vi->flags & FTF_BOOTCPUID) | ||||||
|  | 		fdt->boot_cpuid_phys = cpu_to_fdt32(boot_cpuid_phys); | ||||||
|  | 	if (vi->flags & FTF_STRTABSIZE) | ||||||
|  | 		fdt->size_dt_strings = cpu_to_fdt32(strsize); | ||||||
|  | 	if (vi->flags & FTF_STRUCTSIZE) | ||||||
|  | 		fdt->size_dt_struct = cpu_to_fdt32(dtsize); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void dt_to_blob(FILE *f, struct dt_info *dti, int version) | ||||||
|  | { | ||||||
|  | 	struct version_info *vi = NULL; | ||||||
|  | 	int i; | ||||||
|  | 	struct data blob       = empty_data; | ||||||
|  | 	struct data reservebuf = empty_data; | ||||||
|  | 	struct data dtbuf      = empty_data; | ||||||
|  | 	struct data strbuf     = empty_data; | ||||||
|  | 	struct fdt_header fdt; | ||||||
|  | 	int padlen = 0; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(version_table); i++) { | ||||||
|  | 		if (version_table[i].version == version) | ||||||
|  | 			vi = &version_table[i]; | ||||||
|  | 	} | ||||||
|  | 	if (!vi) | ||||||
|  | 		die("Unknown device tree blob version %d\n", version); | ||||||
|  |  | ||||||
|  | 	flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi); | ||||||
|  | 	bin_emit_cell(&dtbuf, FDT_END); | ||||||
|  |  | ||||||
|  | 	reservebuf = flatten_reserve_list(dti->reservelist, vi); | ||||||
|  |  | ||||||
|  | 	/* Make header */ | ||||||
|  | 	make_fdt_header(&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, | ||||||
|  | 			dti->boot_cpuid_phys); | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * If the user asked for more space than is used, adjust the totalsize. | ||||||
|  | 	 */ | ||||||
|  | 	if (minsize > 0) { | ||||||
|  | 		padlen = minsize - fdt32_to_cpu(fdt.totalsize); | ||||||
|  | 		if (padlen < 0) { | ||||||
|  | 			padlen = 0; | ||||||
|  | 			if (quiet < 1) | ||||||
|  | 				fprintf(stderr, | ||||||
|  | 					"Warning: blob size %d >= minimum size %d\n", | ||||||
|  | 					fdt32_to_cpu(fdt.totalsize), minsize); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (padsize > 0) | ||||||
|  | 		padlen = padsize; | ||||||
|  |  | ||||||
|  | 	if (alignsize > 0) | ||||||
|  | 		padlen = ALIGN(fdt32_to_cpu(fdt.totalsize) + padlen, alignsize) | ||||||
|  | 			- fdt32_to_cpu(fdt.totalsize); | ||||||
|  |  | ||||||
|  | 	if (padlen > 0) { | ||||||
|  | 		int tsize = fdt32_to_cpu(fdt.totalsize); | ||||||
|  | 		tsize += padlen; | ||||||
|  | 		fdt.totalsize = cpu_to_fdt32(tsize); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Assemble the blob: start with the header, add with alignment | ||||||
|  | 	 * the reserve buffer, add the reserve map terminating zeroes, | ||||||
|  | 	 * the device tree itself, and finally the strings. | ||||||
|  | 	 */ | ||||||
|  | 	blob = data_append_data(blob, &fdt, vi->hdr_size); | ||||||
|  | 	blob = data_append_align(blob, 8); | ||||||
|  | 	blob = data_merge(blob, reservebuf); | ||||||
|  | 	blob = data_append_zeroes(blob, sizeof(struct fdt_reserve_entry)); | ||||||
|  | 	blob = data_merge(blob, dtbuf); | ||||||
|  | 	blob = data_merge(blob, strbuf); | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * If the user asked for more space than is used, pad out the blob. | ||||||
|  | 	 */ | ||||||
|  | 	if (padlen > 0) | ||||||
|  | 		blob = data_append_zeroes(blob, padlen); | ||||||
|  |  | ||||||
|  | 	if (fwrite(blob.val, blob.len, 1, f) != 1) { | ||||||
|  | 		if (ferror(f)) | ||||||
|  | 			die("Error writing device tree blob: %s\n", | ||||||
|  | 			    strerror(errno)); | ||||||
|  | 		else | ||||||
|  | 			die("Short write on device tree blob\n"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * data_merge() frees the right-hand element so only the blob | ||||||
|  | 	 * remains to be freed. | ||||||
|  | 	 */ | ||||||
|  | 	data_free(blob); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void dump_stringtable_asm(FILE *f, struct data strbuf) | ||||||
|  | { | ||||||
|  | 	const char *p; | ||||||
|  | 	int len; | ||||||
|  |  | ||||||
|  | 	p = strbuf.val; | ||||||
|  |  | ||||||
|  | 	while (p < (strbuf.val + strbuf.len)) { | ||||||
|  | 		len = strlen(p); | ||||||
|  | 		fprintf(f, "\t.string \"%s\"\n", p); | ||||||
|  | 		p += len+1; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void dt_to_asm(FILE *f, struct dt_info *dti, int version) | ||||||
|  | { | ||||||
|  | 	struct version_info *vi = NULL; | ||||||
|  | 	int i; | ||||||
|  | 	struct data strbuf = empty_data; | ||||||
|  | 	struct reserve_info *re; | ||||||
|  | 	const char *symprefix = "dt"; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < ARRAY_SIZE(version_table); i++) { | ||||||
|  | 		if (version_table[i].version == version) | ||||||
|  | 			vi = &version_table[i]; | ||||||
|  | 	} | ||||||
|  | 	if (!vi) | ||||||
|  | 		die("Unknown device tree blob version %d\n", version); | ||||||
|  |  | ||||||
|  | 	fprintf(f, "/* autogenerated by dtc, do not edit */\n\n"); | ||||||
|  |  | ||||||
|  | 	emit_label(f, symprefix, "blob_start"); | ||||||
|  | 	emit_label(f, symprefix, "header"); | ||||||
|  | 	fprintf(f, "\t/* magic */\n"); | ||||||
|  | 	asm_emit_cell(f, FDT_MAGIC); | ||||||
|  | 	fprintf(f, "\t/* totalsize */\n"); | ||||||
|  | 	ASM_EMIT_BELONG(f, "_%s_blob_abs_end - _%s_blob_start", | ||||||
|  | 			symprefix, symprefix); | ||||||
|  | 	fprintf(f, "\t/* off_dt_struct */\n"); | ||||||
|  | 	ASM_EMIT_BELONG(f, "_%s_struct_start - _%s_blob_start", | ||||||
|  | 		symprefix, symprefix); | ||||||
|  | 	fprintf(f, "\t/* off_dt_strings */\n"); | ||||||
|  | 	ASM_EMIT_BELONG(f, "_%s_strings_start - _%s_blob_start", | ||||||
|  | 		symprefix, symprefix); | ||||||
|  | 	fprintf(f, "\t/* off_mem_rsvmap */\n"); | ||||||
|  | 	ASM_EMIT_BELONG(f, "_%s_reserve_map - _%s_blob_start", | ||||||
|  | 		symprefix, symprefix); | ||||||
|  | 	fprintf(f, "\t/* version */\n"); | ||||||
|  | 	asm_emit_cell(f, vi->version); | ||||||
|  | 	fprintf(f, "\t/* last_comp_version */\n"); | ||||||
|  | 	asm_emit_cell(f, vi->last_comp_version); | ||||||
|  |  | ||||||
|  | 	if (vi->flags & FTF_BOOTCPUID) { | ||||||
|  | 		fprintf(f, "\t/* boot_cpuid_phys */\n"); | ||||||
|  | 		asm_emit_cell(f, dti->boot_cpuid_phys); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (vi->flags & FTF_STRTABSIZE) { | ||||||
|  | 		fprintf(f, "\t/* size_dt_strings */\n"); | ||||||
|  | 		ASM_EMIT_BELONG(f, "_%s_strings_end - _%s_strings_start", | ||||||
|  | 				symprefix, symprefix); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (vi->flags & FTF_STRUCTSIZE) { | ||||||
|  | 		fprintf(f, "\t/* size_dt_struct */\n"); | ||||||
|  | 		ASM_EMIT_BELONG(f, "_%s_struct_end - _%s_struct_start", | ||||||
|  | 			symprefix, symprefix); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Reserve map entries. | ||||||
|  | 	 * Align the reserve map to a doubleword boundary. | ||||||
|  | 	 * Each entry is an (address, size) pair of u64 values. | ||||||
|  | 	 * Always supply a zero-sized temination entry. | ||||||
|  | 	 */ | ||||||
|  | 	asm_emit_align(f, 8); | ||||||
|  | 	emit_label(f, symprefix, "reserve_map"); | ||||||
|  |  | ||||||
|  | 	fprintf(f, "/* Memory reserve map from source file */\n"); | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Use .long on high and low halfs of u64s to avoid .quad | ||||||
|  | 	 * as it appears .quad isn't available in some assemblers. | ||||||
|  | 	 */ | ||||||
|  | 	for (re = dti->reservelist; re; re = re->next) { | ||||||
|  | 		struct label *l; | ||||||
|  |  | ||||||
|  | 		for_each_label(re->labels, l) { | ||||||
|  | 			fprintf(f, "\t.globl\t%s\n", l->label); | ||||||
|  | 			fprintf(f, "%s:\n", l->label); | ||||||
|  | 		} | ||||||
|  | 		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.address >> 32)); | ||||||
|  | 		ASM_EMIT_BELONG(f, "0x%08x", | ||||||
|  | 				(unsigned int)(re->re.address & 0xffffffff)); | ||||||
|  | 		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size >> 32)); | ||||||
|  | 		ASM_EMIT_BELONG(f, "0x%08x", (unsigned int)(re->re.size & 0xffffffff)); | ||||||
|  | 	} | ||||||
|  | 	for (i = 0; i < reservenum; i++) { | ||||||
|  | 		fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n"); | ||||||
|  |  | ||||||
|  | 	emit_label(f, symprefix, "struct_start"); | ||||||
|  | 	flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi); | ||||||
|  |  | ||||||
|  | 	fprintf(f, "\t/* FDT_END */\n"); | ||||||
|  | 	asm_emit_cell(f, FDT_END); | ||||||
|  | 	emit_label(f, symprefix, "struct_end"); | ||||||
|  |  | ||||||
|  | 	emit_label(f, symprefix, "strings_start"); | ||||||
|  | 	dump_stringtable_asm(f, strbuf); | ||||||
|  | 	emit_label(f, symprefix, "strings_end"); | ||||||
|  |  | ||||||
|  | 	emit_label(f, symprefix, "blob_end"); | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * If the user asked for more space than is used, pad it out. | ||||||
|  | 	 */ | ||||||
|  | 	if (minsize > 0) { | ||||||
|  | 		fprintf(f, "\t.space\t%d - (_%s_blob_end - _%s_blob_start), 0\n", | ||||||
|  | 			minsize, symprefix, symprefix); | ||||||
|  | 	} | ||||||
|  | 	if (padsize > 0) { | ||||||
|  | 		fprintf(f, "\t.space\t%d, 0\n", padsize); | ||||||
|  | 	} | ||||||
|  | 	if (alignsize > 0) | ||||||
|  | 		asm_emit_align(f, alignsize); | ||||||
|  | 	emit_label(f, symprefix, "blob_abs_end"); | ||||||
|  |  | ||||||
|  | 	data_free(strbuf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct inbuf { | ||||||
|  | 	char *base, *limit, *ptr; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void inbuf_init(struct inbuf *inb, void *base, void *limit) | ||||||
|  | { | ||||||
|  | 	inb->base = base; | ||||||
|  | 	inb->limit = limit; | ||||||
|  | 	inb->ptr = inb->base; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void flat_read_chunk(struct inbuf *inb, void *p, int len) | ||||||
|  | { | ||||||
|  | 	if ((inb->ptr + len) > inb->limit) | ||||||
|  | 		die("Premature end of data parsing flat device tree\n"); | ||||||
|  |  | ||||||
|  | 	memcpy(p, inb->ptr, len); | ||||||
|  |  | ||||||
|  | 	inb->ptr += len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static uint32_t flat_read_word(struct inbuf *inb) | ||||||
|  | { | ||||||
|  | 	uint32_t val; | ||||||
|  |  | ||||||
|  | 	assert(((inb->ptr - inb->base) % sizeof(val)) == 0); | ||||||
|  |  | ||||||
|  | 	flat_read_chunk(inb, &val, sizeof(val)); | ||||||
|  |  | ||||||
|  | 	return fdt32_to_cpu(val); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void flat_realign(struct inbuf *inb, int align) | ||||||
|  | { | ||||||
|  | 	int off = inb->ptr - inb->base; | ||||||
|  |  | ||||||
|  | 	inb->ptr = inb->base + ALIGN(off, align); | ||||||
|  | 	if (inb->ptr > inb->limit) | ||||||
|  | 		die("Premature end of data parsing flat device tree\n"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static char *flat_read_string(struct inbuf *inb) | ||||||
|  | { | ||||||
|  | 	int len = 0; | ||||||
|  | 	const char *p = inb->ptr; | ||||||
|  | 	char *str; | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		if (p >= inb->limit) | ||||||
|  | 			die("Premature end of data parsing flat device tree\n"); | ||||||
|  | 		len++; | ||||||
|  | 	} while ((*p++) != '\0'); | ||||||
|  |  | ||||||
|  | 	str = xstrdup(inb->ptr); | ||||||
|  |  | ||||||
|  | 	inb->ptr += len; | ||||||
|  |  | ||||||
|  | 	flat_realign(inb, sizeof(uint32_t)); | ||||||
|  |  | ||||||
|  | 	return str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct data flat_read_data(struct inbuf *inb, int len) | ||||||
|  | { | ||||||
|  | 	struct data d = empty_data; | ||||||
|  |  | ||||||
|  | 	if (len == 0) | ||||||
|  | 		return empty_data; | ||||||
|  |  | ||||||
|  | 	d = data_grow_for(d, len); | ||||||
|  | 	d.len = len; | ||||||
|  |  | ||||||
|  | 	flat_read_chunk(inb, d.val, len); | ||||||
|  |  | ||||||
|  | 	flat_realign(inb, sizeof(uint32_t)); | ||||||
|  |  | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static char *flat_read_stringtable(struct inbuf *inb, int offset) | ||||||
|  | { | ||||||
|  | 	const char *p; | ||||||
|  |  | ||||||
|  | 	p = inb->base + offset; | ||||||
|  | 	while (1) { | ||||||
|  | 		if (p >= inb->limit || p < inb->base) | ||||||
|  | 			die("String offset %d overruns string table\n", | ||||||
|  | 			    offset); | ||||||
|  |  | ||||||
|  | 		if (*p == '\0') | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		p++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return xstrdup(inb->base + offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct property *flat_read_property(struct inbuf *dtbuf, | ||||||
|  | 					   struct inbuf *strbuf, int flags) | ||||||
|  | { | ||||||
|  | 	uint32_t proplen, stroff; | ||||||
|  | 	char *name; | ||||||
|  | 	struct data val; | ||||||
|  |  | ||||||
|  | 	proplen = flat_read_word(dtbuf); | ||||||
|  | 	stroff = flat_read_word(dtbuf); | ||||||
|  |  | ||||||
|  | 	name = flat_read_stringtable(strbuf, stroff); | ||||||
|  |  | ||||||
|  | 	if ((flags & FTF_VARALIGN) && (proplen >= 8)) | ||||||
|  | 		flat_realign(dtbuf, 8); | ||||||
|  |  | ||||||
|  | 	val = flat_read_data(dtbuf, proplen); | ||||||
|  |  | ||||||
|  | 	return build_property(name, val); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static struct reserve_info *flat_read_mem_reserve(struct inbuf *inb) | ||||||
|  | { | ||||||
|  | 	struct reserve_info *reservelist = NULL; | ||||||
|  | 	struct reserve_info *new; | ||||||
|  | 	struct fdt_reserve_entry re; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Each entry is a pair of u64 (addr, size) values for 4 cell_t's. | ||||||
|  | 	 * List terminates at an entry with size equal to zero. | ||||||
|  | 	 * | ||||||
|  | 	 * First pass, count entries. | ||||||
|  | 	 */ | ||||||
|  | 	while (1) { | ||||||
|  | 		flat_read_chunk(inb, &re, sizeof(re)); | ||||||
|  | 		re.address  = fdt64_to_cpu(re.address); | ||||||
|  | 		re.size = fdt64_to_cpu(re.size); | ||||||
|  | 		if (re.size == 0) | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		new = build_reserve_entry(re.address, re.size); | ||||||
|  | 		reservelist = add_reserve_entry(reservelist, new); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return reservelist; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static char *nodename_from_path(const char *ppath, const char *cpath) | ||||||
|  | { | ||||||
|  | 	int plen; | ||||||
|  |  | ||||||
|  | 	plen = strlen(ppath); | ||||||
|  |  | ||||||
|  | 	if (!strneq(ppath, cpath, plen)) | ||||||
|  | 		die("Path \"%s\" is not valid as a child of \"%s\"\n", | ||||||
|  | 		    cpath, ppath); | ||||||
|  |  | ||||||
|  | 	/* root node is a special case */ | ||||||
|  | 	if (!streq(ppath, "/")) | ||||||
|  | 		plen++; | ||||||
|  |  | ||||||
|  | 	return xstrdup(cpath + plen); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct node *unflatten_tree(struct inbuf *dtbuf, | ||||||
|  | 				   struct inbuf *strbuf, | ||||||
|  | 				   const char *parent_flatname, int flags) | ||||||
|  | { | ||||||
|  | 	struct node *node; | ||||||
|  | 	char *flatname; | ||||||
|  | 	uint32_t val; | ||||||
|  |  | ||||||
|  | 	node = build_node(NULL, NULL); | ||||||
|  |  | ||||||
|  | 	flatname = flat_read_string(dtbuf); | ||||||
|  |  | ||||||
|  | 	if (flags & FTF_FULLPATH) | ||||||
|  | 		node->name = nodename_from_path(parent_flatname, flatname); | ||||||
|  | 	else | ||||||
|  | 		node->name = flatname; | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		struct property *prop; | ||||||
|  | 		struct node *child; | ||||||
|  |  | ||||||
|  | 		val = flat_read_word(dtbuf); | ||||||
|  | 		switch (val) { | ||||||
|  | 		case FDT_PROP: | ||||||
|  | 			if (node->children) | ||||||
|  | 				fprintf(stderr, "Warning: Flat tree input has " | ||||||
|  | 					"subnodes preceding a property.\n"); | ||||||
|  | 			prop = flat_read_property(dtbuf, strbuf, flags); | ||||||
|  | 			add_property(node, prop); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case FDT_BEGIN_NODE: | ||||||
|  | 			child = unflatten_tree(dtbuf,strbuf, flatname, flags); | ||||||
|  | 			add_child(node, child); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case FDT_END_NODE: | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case FDT_END: | ||||||
|  | 			die("Premature FDT_END in device tree blob\n"); | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case FDT_NOP: | ||||||
|  | 			if (!(flags & FTF_NOPS)) | ||||||
|  | 				fprintf(stderr, "Warning: NOP tag found in flat tree" | ||||||
|  | 					" version <16\n"); | ||||||
|  |  | ||||||
|  | 			/* Ignore */ | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		default: | ||||||
|  | 			die("Invalid opcode word %08x in device tree blob\n", | ||||||
|  | 			    val); | ||||||
|  | 		} | ||||||
|  | 	} while (val != FDT_END_NODE); | ||||||
|  |  | ||||||
|  | 	if (node->name != flatname) { | ||||||
|  | 		free(flatname); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return node; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct dt_info *dt_from_blob(const char *fname) | ||||||
|  | { | ||||||
|  | 	FILE *f; | ||||||
|  | 	uint32_t magic, totalsize, version, size_dt, boot_cpuid_phys; | ||||||
|  | 	uint32_t off_dt, off_str, off_mem_rsvmap; | ||||||
|  | 	int rc; | ||||||
|  | 	char *blob; | ||||||
|  | 	struct fdt_header *fdt; | ||||||
|  | 	char *p; | ||||||
|  | 	struct inbuf dtbuf, strbuf; | ||||||
|  | 	struct inbuf memresvbuf; | ||||||
|  | 	int sizeleft; | ||||||
|  | 	struct reserve_info *reservelist; | ||||||
|  | 	struct node *tree; | ||||||
|  | 	uint32_t val; | ||||||
|  | 	int flags = 0; | ||||||
|  |  | ||||||
|  | 	f = srcfile_relative_open(fname, NULL); | ||||||
|  |  | ||||||
|  | 	rc = fread(&magic, sizeof(magic), 1, f); | ||||||
|  | 	if (ferror(f)) | ||||||
|  | 		die("Error reading DT blob magic number: %s\n", | ||||||
|  | 		    strerror(errno)); | ||||||
|  | 	if (rc < 1) { | ||||||
|  | 		if (feof(f)) | ||||||
|  | 			die("EOF reading DT blob magic number\n"); | ||||||
|  | 		else | ||||||
|  | 			die("Mysterious short read reading magic number\n"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	magic = fdt32_to_cpu(magic); | ||||||
|  | 	if (magic != FDT_MAGIC) | ||||||
|  | 		die("Blob has incorrect magic number\n"); | ||||||
|  |  | ||||||
|  | 	rc = fread(&totalsize, sizeof(totalsize), 1, f); | ||||||
|  | 	if (ferror(f)) | ||||||
|  | 		die("Error reading DT blob size: %s\n", strerror(errno)); | ||||||
|  | 	if (rc < 1) { | ||||||
|  | 		if (feof(f)) | ||||||
|  | 			die("EOF reading DT blob size\n"); | ||||||
|  | 		else | ||||||
|  | 			die("Mysterious short read reading blob size\n"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	totalsize = fdt32_to_cpu(totalsize); | ||||||
|  | 	if (totalsize < FDT_V1_SIZE) | ||||||
|  | 		die("DT blob size (%d) is too small\n", totalsize); | ||||||
|  |  | ||||||
|  | 	blob = xmalloc(totalsize); | ||||||
|  |  | ||||||
|  | 	fdt = (struct fdt_header *)blob; | ||||||
|  | 	fdt->magic = cpu_to_fdt32(magic); | ||||||
|  | 	fdt->totalsize = cpu_to_fdt32(totalsize); | ||||||
|  |  | ||||||
|  | 	sizeleft = totalsize - sizeof(magic) - sizeof(totalsize); | ||||||
|  | 	p = blob + sizeof(magic)  + sizeof(totalsize); | ||||||
|  |  | ||||||
|  | 	while (sizeleft) { | ||||||
|  | 		if (feof(f)) | ||||||
|  | 			die("EOF before reading %d bytes of DT blob\n", | ||||||
|  | 			    totalsize); | ||||||
|  |  | ||||||
|  | 		rc = fread(p, 1, sizeleft, f); | ||||||
|  | 		if (ferror(f)) | ||||||
|  | 			die("Error reading DT blob: %s\n", | ||||||
|  | 			    strerror(errno)); | ||||||
|  |  | ||||||
|  | 		sizeleft -= rc; | ||||||
|  | 		p += rc; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	off_dt = fdt32_to_cpu(fdt->off_dt_struct); | ||||||
|  | 	off_str = fdt32_to_cpu(fdt->off_dt_strings); | ||||||
|  | 	off_mem_rsvmap = fdt32_to_cpu(fdt->off_mem_rsvmap); | ||||||
|  | 	version = fdt32_to_cpu(fdt->version); | ||||||
|  | 	boot_cpuid_phys = fdt32_to_cpu(fdt->boot_cpuid_phys); | ||||||
|  |  | ||||||
|  | 	if (off_mem_rsvmap >= totalsize) | ||||||
|  | 		die("Mem Reserve structure offset exceeds total size\n"); | ||||||
|  |  | ||||||
|  | 	if (off_dt >= totalsize) | ||||||
|  | 		die("DT structure offset exceeds total size\n"); | ||||||
|  |  | ||||||
|  | 	if (off_str > totalsize) | ||||||
|  | 		die("String table offset exceeds total size\n"); | ||||||
|  |  | ||||||
|  | 	if (version >= 3) { | ||||||
|  | 		uint32_t size_str = fdt32_to_cpu(fdt->size_dt_strings); | ||||||
|  | 		if ((off_str+size_str < off_str) || (off_str+size_str > totalsize)) | ||||||
|  | 			die("String table extends past total size\n"); | ||||||
|  | 		inbuf_init(&strbuf, blob + off_str, blob + off_str + size_str); | ||||||
|  | 	} else { | ||||||
|  | 		inbuf_init(&strbuf, blob + off_str, blob + totalsize); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (version >= 17) { | ||||||
|  | 		size_dt = fdt32_to_cpu(fdt->size_dt_struct); | ||||||
|  | 		if ((off_dt+size_dt < off_dt) || (off_dt+size_dt > totalsize)) | ||||||
|  | 			die("Structure block extends past total size\n"); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (version < 16) { | ||||||
|  | 		flags |= FTF_FULLPATH | FTF_NAMEPROPS | FTF_VARALIGN; | ||||||
|  | 	} else { | ||||||
|  | 		flags |= FTF_NOPS; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	inbuf_init(&memresvbuf, | ||||||
|  | 		   blob + off_mem_rsvmap, blob + totalsize); | ||||||
|  | 	inbuf_init(&dtbuf, blob + off_dt, blob + totalsize); | ||||||
|  |  | ||||||
|  | 	reservelist = flat_read_mem_reserve(&memresvbuf); | ||||||
|  |  | ||||||
|  | 	val = flat_read_word(&dtbuf); | ||||||
|  |  | ||||||
|  | 	if (val != FDT_BEGIN_NODE) | ||||||
|  | 		die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val); | ||||||
|  |  | ||||||
|  | 	tree = unflatten_tree(&dtbuf, &strbuf, "", flags); | ||||||
|  |  | ||||||
|  | 	val = flat_read_word(&dtbuf); | ||||||
|  | 	if (val != FDT_END) | ||||||
|  | 		die("Device tree blob doesn't end with FDT_END\n"); | ||||||
|  |  | ||||||
|  | 	free(blob); | ||||||
|  |  | ||||||
|  | 	fclose(f); | ||||||
|  |  | ||||||
|  | 	return build_dt_info(DTSF_V1, reservelist, tree, boot_cpuid_phys); | ||||||
|  | } | ||||||
							
								
								
									
										90
									
								
								scripts/dtc/fstree.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								scripts/dtc/fstree.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  |  | ||||||
|  | #include <dirent.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  |  | ||||||
|  | static struct node *read_fstree(const char *dirname) | ||||||
|  | { | ||||||
|  | 	DIR *d; | ||||||
|  | 	struct dirent *de; | ||||||
|  | 	struct stat st; | ||||||
|  | 	struct node *tree; | ||||||
|  |  | ||||||
|  | 	d = opendir(dirname); | ||||||
|  | 	if (!d) | ||||||
|  | 		die("Couldn't opendir() \"%s\": %s\n", dirname, strerror(errno)); | ||||||
|  |  | ||||||
|  | 	tree = build_node(NULL, NULL); | ||||||
|  |  | ||||||
|  | 	while ((de = readdir(d)) != NULL) { | ||||||
|  | 		char *tmpname; | ||||||
|  |  | ||||||
|  | 		if (streq(de->d_name, ".") | ||||||
|  | 		    || streq(de->d_name, "..")) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		tmpname = join_path(dirname, de->d_name); | ||||||
|  |  | ||||||
|  | 		if (lstat(tmpname, &st) < 0) | ||||||
|  | 			die("stat(%s): %s\n", tmpname, strerror(errno)); | ||||||
|  |  | ||||||
|  | 		if (S_ISREG(st.st_mode)) { | ||||||
|  | 			struct property *prop; | ||||||
|  | 			FILE *pfile; | ||||||
|  |  | ||||||
|  | 			pfile = fopen(tmpname, "rb"); | ||||||
|  | 			if (! pfile) { | ||||||
|  | 				fprintf(stderr, | ||||||
|  | 					"WARNING: Cannot open %s: %s\n", | ||||||
|  | 					tmpname, strerror(errno)); | ||||||
|  | 			} else { | ||||||
|  | 				prop = build_property(xstrdup(de->d_name), | ||||||
|  | 						      data_copy_file(pfile, | ||||||
|  | 								     st.st_size)); | ||||||
|  | 				add_property(tree, prop); | ||||||
|  | 				fclose(pfile); | ||||||
|  | 			} | ||||||
|  | 		} else if (S_ISDIR(st.st_mode)) { | ||||||
|  | 			struct node *newchild; | ||||||
|  |  | ||||||
|  | 			newchild = read_fstree(tmpname); | ||||||
|  | 			newchild = name_node(newchild, xstrdup(de->d_name)); | ||||||
|  | 			add_child(tree, newchild); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		free(tmpname); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	closedir(d); | ||||||
|  | 	return tree; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct dt_info *dt_from_fs(const char *dirname) | ||||||
|  | { | ||||||
|  | 	struct node *tree; | ||||||
|  |  | ||||||
|  | 	tree = read_fstree(dirname); | ||||||
|  | 	tree = name_node(tree, ""); | ||||||
|  |  | ||||||
|  | 	return build_dt_info(DTSF_V1, NULL, tree, guess_boot_cpuid(tree)); | ||||||
|  | } | ||||||
							
								
								
									
										11
									
								
								scripts/dtc/libfdt/Makefile.libfdt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								scripts/dtc/libfdt/Makefile.libfdt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | # Makefile.libfdt | ||||||
|  | # | ||||||
|  | # This is not a complete Makefile of itself.  Instead, it is designed to | ||||||
|  | # be easily embeddable into other systems of Makefiles. | ||||||
|  | # | ||||||
|  | LIBFDT_soname = libfdt.$(SHAREDLIB_EXT).1 | ||||||
|  | LIBFDT_INCLUDES = fdt.h libfdt.h libfdt_env.h | ||||||
|  | LIBFDT_VERSION = version.lds | ||||||
|  | LIBFDT_SRCS = fdt.c fdt_ro.c fdt_wip.c fdt_sw.c fdt_rw.c fdt_strerror.c fdt_empty_tree.c \ | ||||||
|  | 	fdt_addresses.c fdt_overlay.c | ||||||
|  | LIBFDT_OBJS = $(LIBFDT_SRCS:%.c=%.o) | ||||||
							
								
								
									
										251
									
								
								scripts/dtc/libfdt/fdt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								scripts/dtc/libfdt/fdt.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | |||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include "libfdt_env.h" | ||||||
|  |  | ||||||
|  | #include <fdt.h> | ||||||
|  | #include <libfdt.h> | ||||||
|  |  | ||||||
|  | #include "libfdt_internal.h" | ||||||
|  |  | ||||||
|  | int fdt_check_header(const void *fdt) | ||||||
|  | { | ||||||
|  | 	if (fdt_magic(fdt) == FDT_MAGIC) { | ||||||
|  | 		/* Complete tree */ | ||||||
|  | 		if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) | ||||||
|  | 			return -FDT_ERR_BADVERSION; | ||||||
|  | 		if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) | ||||||
|  | 			return -FDT_ERR_BADVERSION; | ||||||
|  | 	} else if (fdt_magic(fdt) == FDT_SW_MAGIC) { | ||||||
|  | 		/* Unfinished sequential-write blob */ | ||||||
|  | 		if (fdt_size_dt_struct(fdt) == 0) | ||||||
|  | 			return -FDT_ERR_BADSTATE; | ||||||
|  | 	} else { | ||||||
|  | 		return -FDT_ERR_BADMAGIC; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) | ||||||
|  | { | ||||||
|  | 	unsigned absoffset = offset + fdt_off_dt_struct(fdt); | ||||||
|  |  | ||||||
|  | 	if ((absoffset < offset) | ||||||
|  | 	    || ((absoffset + len) < absoffset) | ||||||
|  | 	    || (absoffset + len) > fdt_totalsize(fdt)) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (fdt_version(fdt) >= 0x11) | ||||||
|  | 		if (((offset + len) < offset) | ||||||
|  | 		    || ((offset + len) > fdt_size_dt_struct(fdt))) | ||||||
|  | 			return NULL; | ||||||
|  |  | ||||||
|  | 	return _fdt_offset_ptr(fdt, offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) | ||||||
|  | { | ||||||
|  | 	const fdt32_t *tagp, *lenp; | ||||||
|  | 	uint32_t tag; | ||||||
|  | 	int offset = startoffset; | ||||||
|  | 	const char *p; | ||||||
|  |  | ||||||
|  | 	*nextoffset = -FDT_ERR_TRUNCATED; | ||||||
|  | 	tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); | ||||||
|  | 	if (!tagp) | ||||||
|  | 		return FDT_END; /* premature end */ | ||||||
|  | 	tag = fdt32_to_cpu(*tagp); | ||||||
|  | 	offset += FDT_TAGSIZE; | ||||||
|  |  | ||||||
|  | 	*nextoffset = -FDT_ERR_BADSTRUCTURE; | ||||||
|  | 	switch (tag) { | ||||||
|  | 	case FDT_BEGIN_NODE: | ||||||
|  | 		/* skip name */ | ||||||
|  | 		do { | ||||||
|  | 			p = fdt_offset_ptr(fdt, offset++, 1); | ||||||
|  | 		} while (p && (*p != '\0')); | ||||||
|  | 		if (!p) | ||||||
|  | 			return FDT_END; /* premature end */ | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	case FDT_PROP: | ||||||
|  | 		lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); | ||||||
|  | 		if (!lenp) | ||||||
|  | 			return FDT_END; /* premature end */ | ||||||
|  | 		/* skip-name offset, length and value */ | ||||||
|  | 		offset += sizeof(struct fdt_property) - FDT_TAGSIZE | ||||||
|  | 			+ fdt32_to_cpu(*lenp); | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	case FDT_END: | ||||||
|  | 	case FDT_END_NODE: | ||||||
|  | 	case FDT_NOP: | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		return FDT_END; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) | ||||||
|  | 		return FDT_END; /* premature end */ | ||||||
|  |  | ||||||
|  | 	*nextoffset = FDT_TAGALIGN(offset); | ||||||
|  | 	return tag; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int _fdt_check_node_offset(const void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	if ((offset < 0) || (offset % FDT_TAGSIZE) | ||||||
|  | 	    || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) | ||||||
|  | 		return -FDT_ERR_BADOFFSET; | ||||||
|  |  | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int _fdt_check_prop_offset(const void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	if ((offset < 0) || (offset % FDT_TAGSIZE) | ||||||
|  | 	    || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) | ||||||
|  | 		return -FDT_ERR_BADOFFSET; | ||||||
|  |  | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_next_node(const void *fdt, int offset, int *depth) | ||||||
|  | { | ||||||
|  | 	int nextoffset = 0; | ||||||
|  | 	uint32_t tag; | ||||||
|  |  | ||||||
|  | 	if (offset >= 0) | ||||||
|  | 		if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) | ||||||
|  | 			return nextoffset; | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		offset = nextoffset; | ||||||
|  | 		tag = fdt_next_tag(fdt, offset, &nextoffset); | ||||||
|  |  | ||||||
|  | 		switch (tag) { | ||||||
|  | 		case FDT_PROP: | ||||||
|  | 		case FDT_NOP: | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case FDT_BEGIN_NODE: | ||||||
|  | 			if (depth) | ||||||
|  | 				(*depth)++; | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case FDT_END_NODE: | ||||||
|  | 			if (depth && ((--(*depth)) < 0)) | ||||||
|  | 				return nextoffset; | ||||||
|  | 			break; | ||||||
|  |  | ||||||
|  | 		case FDT_END: | ||||||
|  | 			if ((nextoffset >= 0) | ||||||
|  | 			    || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) | ||||||
|  | 				return -FDT_ERR_NOTFOUND; | ||||||
|  | 			else | ||||||
|  | 				return nextoffset; | ||||||
|  | 		} | ||||||
|  | 	} while (tag != FDT_BEGIN_NODE); | ||||||
|  |  | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_first_subnode(const void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	int depth = 0; | ||||||
|  |  | ||||||
|  | 	offset = fdt_next_node(fdt, offset, &depth); | ||||||
|  | 	if (offset < 0 || depth != 1) | ||||||
|  | 		return -FDT_ERR_NOTFOUND; | ||||||
|  |  | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_next_subnode(const void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	int depth = 1; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * With respect to the parent, the depth of the next subnode will be | ||||||
|  | 	 * the same as the last. | ||||||
|  | 	 */ | ||||||
|  | 	do { | ||||||
|  | 		offset = fdt_next_node(fdt, offset, &depth); | ||||||
|  | 		if (offset < 0 || depth < 1) | ||||||
|  | 			return -FDT_ERR_NOTFOUND; | ||||||
|  | 	} while (depth > 1); | ||||||
|  |  | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) | ||||||
|  | { | ||||||
|  | 	int len = strlen(s) + 1; | ||||||
|  | 	const char *last = strtab + tabsize - len; | ||||||
|  | 	const char *p; | ||||||
|  |  | ||||||
|  | 	for (p = strtab; p <= last; p++) | ||||||
|  | 		if (memcmp(p, s, len) == 0) | ||||||
|  | 			return p; | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_move(const void *fdt, void *buf, int bufsize) | ||||||
|  | { | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	if (fdt_totalsize(fdt) > bufsize) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	memmove(buf, fdt, fdt_totalsize(fdt)); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										111
									
								
								scripts/dtc/libfdt/fdt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								scripts/dtc/libfdt/fdt.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | #ifndef _FDT_H | ||||||
|  | #define _FDT_H | ||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * Copyright 2012 Kim Phillips, Freescale Semiconductor. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef __ASSEMBLY__ | ||||||
|  |  | ||||||
|  | struct fdt_header { | ||||||
|  | 	fdt32_t magic;			 /* magic word FDT_MAGIC */ | ||||||
|  | 	fdt32_t totalsize;		 /* total size of DT block */ | ||||||
|  | 	fdt32_t off_dt_struct;		 /* offset to structure */ | ||||||
|  | 	fdt32_t off_dt_strings;		 /* offset to strings */ | ||||||
|  | 	fdt32_t off_mem_rsvmap;		 /* offset to memory reserve map */ | ||||||
|  | 	fdt32_t version;		 /* format version */ | ||||||
|  | 	fdt32_t last_comp_version;	 /* last compatible version */ | ||||||
|  |  | ||||||
|  | 	/* version 2 fields below */ | ||||||
|  | 	fdt32_t boot_cpuid_phys;	 /* Which physical CPU id we're | ||||||
|  | 					    booting on */ | ||||||
|  | 	/* version 3 fields below */ | ||||||
|  | 	fdt32_t size_dt_strings;	 /* size of the strings block */ | ||||||
|  |  | ||||||
|  | 	/* version 17 fields below */ | ||||||
|  | 	fdt32_t size_dt_struct;		 /* size of the structure block */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct fdt_reserve_entry { | ||||||
|  | 	fdt64_t address; | ||||||
|  | 	fdt64_t size; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct fdt_node_header { | ||||||
|  | 	fdt32_t tag; | ||||||
|  | 	char name[0]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct fdt_property { | ||||||
|  | 	fdt32_t tag; | ||||||
|  | 	fdt32_t len; | ||||||
|  | 	fdt32_t nameoff; | ||||||
|  | 	char data[0]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #endif /* !__ASSEMBLY */ | ||||||
|  |  | ||||||
|  | #define FDT_MAGIC	0xd00dfeed	/* 4: version, 4: total size */ | ||||||
|  | #define FDT_TAGSIZE	sizeof(fdt32_t) | ||||||
|  |  | ||||||
|  | #define FDT_BEGIN_NODE	0x1		/* Start node: full name */ | ||||||
|  | #define FDT_END_NODE	0x2		/* End node */ | ||||||
|  | #define FDT_PROP	0x3		/* Property: name off, | ||||||
|  | 					   size, content */ | ||||||
|  | #define FDT_NOP		0x4		/* nop */ | ||||||
|  | #define FDT_END		0x9 | ||||||
|  |  | ||||||
|  | #define FDT_V1_SIZE	(7*sizeof(fdt32_t)) | ||||||
|  | #define FDT_V2_SIZE	(FDT_V1_SIZE + sizeof(fdt32_t)) | ||||||
|  | #define FDT_V3_SIZE	(FDT_V2_SIZE + sizeof(fdt32_t)) | ||||||
|  | #define FDT_V16_SIZE	FDT_V3_SIZE | ||||||
|  | #define FDT_V17_SIZE	(FDT_V16_SIZE + sizeof(fdt32_t)) | ||||||
|  |  | ||||||
|  | #endif /* _FDT_H */ | ||||||
							
								
								
									
										84
									
								
								scripts/dtc/libfdt/fdt_empty_tree.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								scripts/dtc/libfdt/fdt_empty_tree.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2012 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include "libfdt_env.h" | ||||||
|  |  | ||||||
|  | #include <fdt.h> | ||||||
|  | #include <libfdt.h> | ||||||
|  |  | ||||||
|  | #include "libfdt_internal.h" | ||||||
|  |  | ||||||
|  | int fdt_create_empty_tree(void *buf, int bufsize) | ||||||
|  | { | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	err = fdt_create(buf, bufsize); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  |  | ||||||
|  | 	err = fdt_finish_reservemap(buf); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  |  | ||||||
|  | 	err = fdt_begin_node(buf, ""); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  |  | ||||||
|  | 	err =  fdt_end_node(buf); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  |  | ||||||
|  | 	err = fdt_finish(buf); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  |  | ||||||
|  | 	return fdt_open_into(buf, buf, bufsize); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										703
									
								
								scripts/dtc/libfdt/fdt_ro.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										703
									
								
								scripts/dtc/libfdt/fdt_ro.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,703 @@ | |||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include "libfdt_env.h" | ||||||
|  |  | ||||||
|  | #include <fdt.h> | ||||||
|  | #include <libfdt.h> | ||||||
|  |  | ||||||
|  | #include "libfdt_internal.h" | ||||||
|  |  | ||||||
|  | static int _fdt_nodename_eq(const void *fdt, int offset, | ||||||
|  | 			    const char *s, int len) | ||||||
|  | { | ||||||
|  | 	const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); | ||||||
|  |  | ||||||
|  | 	if (! p) | ||||||
|  | 		/* short match */ | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	if (memcmp(p, s, len) != 0) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	if (p[len] == '\0') | ||||||
|  | 		return 1; | ||||||
|  | 	else if (!memchr(s, '@', len) && (p[len] == '@')) | ||||||
|  | 		return 1; | ||||||
|  | 	else | ||||||
|  | 		return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *fdt_string(const void *fdt, int stroffset) | ||||||
|  | { | ||||||
|  | 	return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int _fdt_string_eq(const void *fdt, int stroffset, | ||||||
|  | 			  const char *s, int len) | ||||||
|  | { | ||||||
|  | 	const char *p = fdt_string(fdt, stroffset); | ||||||
|  |  | ||||||
|  | 	return (strlen(p) == len) && (memcmp(p, s, len) == 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t fdt_get_max_phandle(const void *fdt) | ||||||
|  | { | ||||||
|  | 	uint32_t max_phandle = 0; | ||||||
|  | 	int offset; | ||||||
|  |  | ||||||
|  | 	for (offset = fdt_next_node(fdt, -1, NULL);; | ||||||
|  | 	     offset = fdt_next_node(fdt, offset, NULL)) { | ||||||
|  | 		uint32_t phandle; | ||||||
|  |  | ||||||
|  | 		if (offset == -FDT_ERR_NOTFOUND) | ||||||
|  | 			return max_phandle; | ||||||
|  |  | ||||||
|  | 		if (offset < 0) | ||||||
|  | 			return (uint32_t)-1; | ||||||
|  |  | ||||||
|  | 		phandle = fdt_get_phandle(fdt, offset); | ||||||
|  | 		if (phandle == (uint32_t)-1) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		if (phandle > max_phandle) | ||||||
|  | 			max_phandle = phandle; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) | ||||||
|  | { | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  | 	*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); | ||||||
|  | 	*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_num_mem_rsv(const void *fdt) | ||||||
|  | { | ||||||
|  | 	int i = 0; | ||||||
|  |  | ||||||
|  | 	while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) | ||||||
|  | 		i++; | ||||||
|  | 	return i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int _nextprop(const void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	uint32_t tag; | ||||||
|  | 	int nextoffset; | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		tag = fdt_next_tag(fdt, offset, &nextoffset); | ||||||
|  |  | ||||||
|  | 		switch (tag) { | ||||||
|  | 		case FDT_END: | ||||||
|  | 			if (nextoffset >= 0) | ||||||
|  | 				return -FDT_ERR_BADSTRUCTURE; | ||||||
|  | 			else | ||||||
|  | 				return nextoffset; | ||||||
|  |  | ||||||
|  | 		case FDT_PROP: | ||||||
|  | 			return offset; | ||||||
|  | 		} | ||||||
|  | 		offset = nextoffset; | ||||||
|  | 	} while (tag == FDT_NOP); | ||||||
|  |  | ||||||
|  | 	return -FDT_ERR_NOTFOUND; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_subnode_offset_namelen(const void *fdt, int offset, | ||||||
|  | 			       const char *name, int namelen) | ||||||
|  | { | ||||||
|  | 	int depth; | ||||||
|  |  | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	for (depth = 0; | ||||||
|  | 	     (offset >= 0) && (depth >= 0); | ||||||
|  | 	     offset = fdt_next_node(fdt, offset, &depth)) | ||||||
|  | 		if ((depth == 1) | ||||||
|  | 		    && _fdt_nodename_eq(fdt, offset, name, namelen)) | ||||||
|  | 			return offset; | ||||||
|  |  | ||||||
|  | 	if (depth < 0) | ||||||
|  | 		return -FDT_ERR_NOTFOUND; | ||||||
|  | 	return offset; /* error */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_subnode_offset(const void *fdt, int parentoffset, | ||||||
|  | 		       const char *name) | ||||||
|  | { | ||||||
|  | 	return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) | ||||||
|  | { | ||||||
|  | 	const char *end = path + namelen; | ||||||
|  | 	const char *p = path; | ||||||
|  | 	int offset = 0; | ||||||
|  |  | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	/* see if we have an alias */ | ||||||
|  | 	if (*path != '/') { | ||||||
|  | 		const char *q = memchr(path, '/', end - p); | ||||||
|  |  | ||||||
|  | 		if (!q) | ||||||
|  | 			q = end; | ||||||
|  |  | ||||||
|  | 		p = fdt_get_alias_namelen(fdt, p, q - p); | ||||||
|  | 		if (!p) | ||||||
|  | 			return -FDT_ERR_BADPATH; | ||||||
|  | 		offset = fdt_path_offset(fdt, p); | ||||||
|  |  | ||||||
|  | 		p = q; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (p < end) { | ||||||
|  | 		const char *q; | ||||||
|  |  | ||||||
|  | 		while (*p == '/') { | ||||||
|  | 			p++; | ||||||
|  | 			if (p == end) | ||||||
|  | 				return offset; | ||||||
|  | 		} | ||||||
|  | 		q = memchr(p, '/', end - p); | ||||||
|  | 		if (! q) | ||||||
|  | 			q = end; | ||||||
|  |  | ||||||
|  | 		offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); | ||||||
|  | 		if (offset < 0) | ||||||
|  | 			return offset; | ||||||
|  |  | ||||||
|  | 		p = q; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_path_offset(const void *fdt, const char *path) | ||||||
|  | { | ||||||
|  | 	return fdt_path_offset_namelen(fdt, path, strlen(path)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) | ||||||
|  | { | ||||||
|  | 	const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	if (((err = fdt_check_header(fdt)) != 0) | ||||||
|  | 	    || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) | ||||||
|  | 			goto fail; | ||||||
|  |  | ||||||
|  | 	if (len) | ||||||
|  | 		*len = strlen(nh->name); | ||||||
|  |  | ||||||
|  | 	return nh->name; | ||||||
|  |  | ||||||
|  |  fail: | ||||||
|  | 	if (len) | ||||||
|  | 		*len = err; | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_first_property_offset(const void *fdt, int nodeoffset) | ||||||
|  | { | ||||||
|  | 	int offset; | ||||||
|  |  | ||||||
|  | 	if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) | ||||||
|  | 		return offset; | ||||||
|  |  | ||||||
|  | 	return _nextprop(fdt, offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_next_property_offset(const void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) | ||||||
|  | 		return offset; | ||||||
|  |  | ||||||
|  | 	return _nextprop(fdt, offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const struct fdt_property *fdt_get_property_by_offset(const void *fdt, | ||||||
|  | 						      int offset, | ||||||
|  | 						      int *lenp) | ||||||
|  | { | ||||||
|  | 	int err; | ||||||
|  | 	const struct fdt_property *prop; | ||||||
|  |  | ||||||
|  | 	if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { | ||||||
|  | 		if (lenp) | ||||||
|  | 			*lenp = err; | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	prop = _fdt_offset_ptr(fdt, offset); | ||||||
|  |  | ||||||
|  | 	if (lenp) | ||||||
|  | 		*lenp = fdt32_to_cpu(prop->len); | ||||||
|  |  | ||||||
|  | 	return prop; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const struct fdt_property *fdt_get_property_namelen(const void *fdt, | ||||||
|  | 						    int offset, | ||||||
|  | 						    const char *name, | ||||||
|  | 						    int namelen, int *lenp) | ||||||
|  | { | ||||||
|  | 	for (offset = fdt_first_property_offset(fdt, offset); | ||||||
|  | 	     (offset >= 0); | ||||||
|  | 	     (offset = fdt_next_property_offset(fdt, offset))) { | ||||||
|  | 		const struct fdt_property *prop; | ||||||
|  |  | ||||||
|  | 		if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { | ||||||
|  | 			offset = -FDT_ERR_INTERNAL; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), | ||||||
|  | 				   name, namelen)) | ||||||
|  | 			return prop; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (lenp) | ||||||
|  | 		*lenp = offset; | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const struct fdt_property *fdt_get_property(const void *fdt, | ||||||
|  | 					    int nodeoffset, | ||||||
|  | 					    const char *name, int *lenp) | ||||||
|  | { | ||||||
|  | 	return fdt_get_property_namelen(fdt, nodeoffset, name, | ||||||
|  | 					strlen(name), lenp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, | ||||||
|  | 				const char *name, int namelen, int *lenp) | ||||||
|  | { | ||||||
|  | 	const struct fdt_property *prop; | ||||||
|  |  | ||||||
|  | 	prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); | ||||||
|  | 	if (! prop) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	return prop->data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const void *fdt_getprop_by_offset(const void *fdt, int offset, | ||||||
|  | 				  const char **namep, int *lenp) | ||||||
|  | { | ||||||
|  | 	const struct fdt_property *prop; | ||||||
|  |  | ||||||
|  | 	prop = fdt_get_property_by_offset(fdt, offset, lenp); | ||||||
|  | 	if (!prop) | ||||||
|  | 		return NULL; | ||||||
|  | 	if (namep) | ||||||
|  | 		*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); | ||||||
|  | 	return prop->data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const void *fdt_getprop(const void *fdt, int nodeoffset, | ||||||
|  | 			const char *name, int *lenp) | ||||||
|  | { | ||||||
|  | 	return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) | ||||||
|  | { | ||||||
|  | 	const fdt32_t *php; | ||||||
|  | 	int len; | ||||||
|  |  | ||||||
|  | 	/* FIXME: This is a bit sub-optimal, since we potentially scan | ||||||
|  | 	 * over all the properties twice. */ | ||||||
|  | 	php = fdt_getprop(fdt, nodeoffset, "phandle", &len); | ||||||
|  | 	if (!php || (len != sizeof(*php))) { | ||||||
|  | 		php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); | ||||||
|  | 		if (!php || (len != sizeof(*php))) | ||||||
|  | 			return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fdt32_to_cpu(*php); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *fdt_get_alias_namelen(const void *fdt, | ||||||
|  | 				  const char *name, int namelen) | ||||||
|  | { | ||||||
|  | 	int aliasoffset; | ||||||
|  |  | ||||||
|  | 	aliasoffset = fdt_path_offset(fdt, "/aliases"); | ||||||
|  | 	if (aliasoffset < 0) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *fdt_get_alias(const void *fdt, const char *name) | ||||||
|  | { | ||||||
|  | 	return fdt_get_alias_namelen(fdt, name, strlen(name)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) | ||||||
|  | { | ||||||
|  | 	int pdepth = 0, p = 0; | ||||||
|  | 	int offset, depth, namelen; | ||||||
|  | 	const char *name; | ||||||
|  |  | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	if (buflen < 2) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	for (offset = 0, depth = 0; | ||||||
|  | 	     (offset >= 0) && (offset <= nodeoffset); | ||||||
|  | 	     offset = fdt_next_node(fdt, offset, &depth)) { | ||||||
|  | 		while (pdepth > depth) { | ||||||
|  | 			do { | ||||||
|  | 				p--; | ||||||
|  | 			} while (buf[p-1] != '/'); | ||||||
|  | 			pdepth--; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (pdepth >= depth) { | ||||||
|  | 			name = fdt_get_name(fdt, offset, &namelen); | ||||||
|  | 			if (!name) | ||||||
|  | 				return namelen; | ||||||
|  | 			if ((p + namelen + 1) <= buflen) { | ||||||
|  | 				memcpy(buf + p, name, namelen); | ||||||
|  | 				p += namelen; | ||||||
|  | 				buf[p++] = '/'; | ||||||
|  | 				pdepth++; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (offset == nodeoffset) { | ||||||
|  | 			if (pdepth < (depth + 1)) | ||||||
|  | 				return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 			if (p > 1) /* special case so that root path is "/", not "" */ | ||||||
|  | 				p--; | ||||||
|  | 			buf[p] = '\0'; | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) | ||||||
|  | 		return -FDT_ERR_BADOFFSET; | ||||||
|  | 	else if (offset == -FDT_ERR_BADOFFSET) | ||||||
|  | 		return -FDT_ERR_BADSTRUCTURE; | ||||||
|  |  | ||||||
|  | 	return offset; /* error from fdt_next_node() */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, | ||||||
|  | 				 int supernodedepth, int *nodedepth) | ||||||
|  | { | ||||||
|  | 	int offset, depth; | ||||||
|  | 	int supernodeoffset = -FDT_ERR_INTERNAL; | ||||||
|  |  | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	if (supernodedepth < 0) | ||||||
|  | 		return -FDT_ERR_NOTFOUND; | ||||||
|  |  | ||||||
|  | 	for (offset = 0, depth = 0; | ||||||
|  | 	     (offset >= 0) && (offset <= nodeoffset); | ||||||
|  | 	     offset = fdt_next_node(fdt, offset, &depth)) { | ||||||
|  | 		if (depth == supernodedepth) | ||||||
|  | 			supernodeoffset = offset; | ||||||
|  |  | ||||||
|  | 		if (offset == nodeoffset) { | ||||||
|  | 			if (nodedepth) | ||||||
|  | 				*nodedepth = depth; | ||||||
|  |  | ||||||
|  | 			if (supernodedepth > depth) | ||||||
|  | 				return -FDT_ERR_NOTFOUND; | ||||||
|  | 			else | ||||||
|  | 				return supernodeoffset; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) | ||||||
|  | 		return -FDT_ERR_BADOFFSET; | ||||||
|  | 	else if (offset == -FDT_ERR_BADOFFSET) | ||||||
|  | 		return -FDT_ERR_BADSTRUCTURE; | ||||||
|  |  | ||||||
|  | 	return offset; /* error from fdt_next_node() */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_node_depth(const void *fdt, int nodeoffset) | ||||||
|  | { | ||||||
|  | 	int nodedepth; | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); | ||||||
|  | 	if (err) | ||||||
|  | 		return (err < 0) ? err : -FDT_ERR_INTERNAL; | ||||||
|  | 	return nodedepth; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_parent_offset(const void *fdt, int nodeoffset) | ||||||
|  | { | ||||||
|  | 	int nodedepth = fdt_node_depth(fdt, nodeoffset); | ||||||
|  |  | ||||||
|  | 	if (nodedepth < 0) | ||||||
|  | 		return nodedepth; | ||||||
|  | 	return fdt_supernode_atdepth_offset(fdt, nodeoffset, | ||||||
|  | 					    nodedepth - 1, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, | ||||||
|  | 				  const char *propname, | ||||||
|  | 				  const void *propval, int proplen) | ||||||
|  | { | ||||||
|  | 	int offset; | ||||||
|  | 	const void *val; | ||||||
|  | 	int len; | ||||||
|  |  | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	/* FIXME: The algorithm here is pretty horrible: we scan each | ||||||
|  | 	 * property of a node in fdt_getprop(), then if that didn't | ||||||
|  | 	 * find what we want, we scan over them again making our way | ||||||
|  | 	 * to the next node.  Still it's the easiest to implement | ||||||
|  | 	 * approach; performance can come later. */ | ||||||
|  | 	for (offset = fdt_next_node(fdt, startoffset, NULL); | ||||||
|  | 	     offset >= 0; | ||||||
|  | 	     offset = fdt_next_node(fdt, offset, NULL)) { | ||||||
|  | 		val = fdt_getprop(fdt, offset, propname, &len); | ||||||
|  | 		if (val && (len == proplen) | ||||||
|  | 		    && (memcmp(val, propval, len) == 0)) | ||||||
|  | 			return offset; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return offset; /* error from fdt_next_node() */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) | ||||||
|  | { | ||||||
|  | 	int offset; | ||||||
|  |  | ||||||
|  | 	if ((phandle == 0) || (phandle == -1)) | ||||||
|  | 		return -FDT_ERR_BADPHANDLE; | ||||||
|  |  | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	/* FIXME: The algorithm here is pretty horrible: we | ||||||
|  | 	 * potentially scan each property of a node in | ||||||
|  | 	 * fdt_get_phandle(), then if that didn't find what | ||||||
|  | 	 * we want, we scan over them again making our way to the next | ||||||
|  | 	 * node.  Still it's the easiest to implement approach; | ||||||
|  | 	 * performance can come later. */ | ||||||
|  | 	for (offset = fdt_next_node(fdt, -1, NULL); | ||||||
|  | 	     offset >= 0; | ||||||
|  | 	     offset = fdt_next_node(fdt, offset, NULL)) { | ||||||
|  | 		if (fdt_get_phandle(fdt, offset) == phandle) | ||||||
|  | 			return offset; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return offset; /* error from fdt_next_node() */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) | ||||||
|  | { | ||||||
|  | 	int len = strlen(str); | ||||||
|  | 	const char *p; | ||||||
|  |  | ||||||
|  | 	while (listlen >= len) { | ||||||
|  | 		if (memcmp(str, strlist, len+1) == 0) | ||||||
|  | 			return 1; | ||||||
|  | 		p = memchr(strlist, '\0', listlen); | ||||||
|  | 		if (!p) | ||||||
|  | 			return 0; /* malformed strlist.. */ | ||||||
|  | 		listlen -= (p-strlist) + 1; | ||||||
|  | 		strlist = p + 1; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) | ||||||
|  | { | ||||||
|  | 	const char *list, *end; | ||||||
|  | 	int length, count = 0; | ||||||
|  |  | ||||||
|  | 	list = fdt_getprop(fdt, nodeoffset, property, &length); | ||||||
|  | 	if (!list) | ||||||
|  | 		return length; | ||||||
|  |  | ||||||
|  | 	end = list + length; | ||||||
|  |  | ||||||
|  | 	while (list < end) { | ||||||
|  | 		length = strnlen(list, end - list) + 1; | ||||||
|  |  | ||||||
|  | 		/* Abort if the last string isn't properly NUL-terminated. */ | ||||||
|  | 		if (list + length > end) | ||||||
|  | 			return -FDT_ERR_BADVALUE; | ||||||
|  |  | ||||||
|  | 		list += length; | ||||||
|  | 		count++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return count; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, | ||||||
|  | 			  const char *string) | ||||||
|  | { | ||||||
|  | 	int length, len, idx = 0; | ||||||
|  | 	const char *list, *end; | ||||||
|  |  | ||||||
|  | 	list = fdt_getprop(fdt, nodeoffset, property, &length); | ||||||
|  | 	if (!list) | ||||||
|  | 		return length; | ||||||
|  |  | ||||||
|  | 	len = strlen(string) + 1; | ||||||
|  | 	end = list + length; | ||||||
|  |  | ||||||
|  | 	while (list < end) { | ||||||
|  | 		length = strnlen(list, end - list) + 1; | ||||||
|  |  | ||||||
|  | 		/* Abort if the last string isn't properly NUL-terminated. */ | ||||||
|  | 		if (list + length > end) | ||||||
|  | 			return -FDT_ERR_BADVALUE; | ||||||
|  |  | ||||||
|  | 		if (length == len && memcmp(list, string, length) == 0) | ||||||
|  | 			return idx; | ||||||
|  |  | ||||||
|  | 		list += length; | ||||||
|  | 		idx++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return -FDT_ERR_NOTFOUND; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *fdt_stringlist_get(const void *fdt, int nodeoffset, | ||||||
|  | 			       const char *property, int idx, | ||||||
|  | 			       int *lenp) | ||||||
|  | { | ||||||
|  | 	const char *list, *end; | ||||||
|  | 	int length; | ||||||
|  |  | ||||||
|  | 	list = fdt_getprop(fdt, nodeoffset, property, &length); | ||||||
|  | 	if (!list) { | ||||||
|  | 		if (lenp) | ||||||
|  | 			*lenp = length; | ||||||
|  |  | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	end = list + length; | ||||||
|  |  | ||||||
|  | 	while (list < end) { | ||||||
|  | 		length = strnlen(list, end - list) + 1; | ||||||
|  |  | ||||||
|  | 		/* Abort if the last string isn't properly NUL-terminated. */ | ||||||
|  | 		if (list + length > end) { | ||||||
|  | 			if (lenp) | ||||||
|  | 				*lenp = -FDT_ERR_BADVALUE; | ||||||
|  |  | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (idx == 0) { | ||||||
|  | 			if (lenp) | ||||||
|  | 				*lenp = length - 1; | ||||||
|  |  | ||||||
|  | 			return list; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		list += length; | ||||||
|  | 		idx--; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (lenp) | ||||||
|  | 		*lenp = -FDT_ERR_NOTFOUND; | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_node_check_compatible(const void *fdt, int nodeoffset, | ||||||
|  | 			      const char *compatible) | ||||||
|  | { | ||||||
|  | 	const void *prop; | ||||||
|  | 	int len; | ||||||
|  |  | ||||||
|  | 	prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); | ||||||
|  | 	if (!prop) | ||||||
|  | 		return len; | ||||||
|  |  | ||||||
|  | 	return !fdt_stringlist_contains(prop, len, compatible); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_node_offset_by_compatible(const void *fdt, int startoffset, | ||||||
|  | 				  const char *compatible) | ||||||
|  | { | ||||||
|  | 	int offset, err; | ||||||
|  |  | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	/* FIXME: The algorithm here is pretty horrible: we scan each | ||||||
|  | 	 * property of a node in fdt_node_check_compatible(), then if | ||||||
|  | 	 * that didn't find what we want, we scan over them again | ||||||
|  | 	 * making our way to the next node.  Still it's the easiest to | ||||||
|  | 	 * implement approach; performance can come later. */ | ||||||
|  | 	for (offset = fdt_next_node(fdt, startoffset, NULL); | ||||||
|  | 	     offset >= 0; | ||||||
|  | 	     offset = fdt_next_node(fdt, offset, NULL)) { | ||||||
|  | 		err = fdt_node_check_compatible(fdt, offset, compatible); | ||||||
|  | 		if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) | ||||||
|  | 			return err; | ||||||
|  | 		else if (err == 0) | ||||||
|  | 			return offset; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return offset; /* error from fdt_next_node() */ | ||||||
|  | } | ||||||
							
								
								
									
										491
									
								
								scripts/dtc/libfdt/fdt_rw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								scripts/dtc/libfdt/fdt_rw.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,491 @@ | |||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #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 int _fdt_rw_check_header(void *fdt) | ||||||
|  | { | ||||||
|  | 	FDT_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	if (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; | ||||||
|  | 	if (fdt_version(fdt) > 17) | ||||||
|  | 		fdt_set_version(fdt, 17); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define FDT_RW_CHECK_HEADER(fdt) \ | ||||||
|  | 	{ \ | ||||||
|  | 		int __err; \ | ||||||
|  | 		if ((__err = _fdt_rw_check_header(fdt)) != 0) \ | ||||||
|  | 			return __err; \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | static inline 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; | ||||||
|  | 	char *end = (char *)fdt + _fdt_data_size(fdt); | ||||||
|  |  | ||||||
|  | 	if (((p + oldlen) < p) || ((p + oldlen) > end)) | ||||||
|  | 		return -FDT_ERR_BADOFFSET; | ||||||
|  | 	if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) | ||||||
|  | 		return -FDT_ERR_BADOFFSET; | ||||||
|  | 	if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  | 	memmove(p + newlen, p + oldlen, end - 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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int _fdt_find_add_string(void *fdt, const char *s) | ||||||
|  | { | ||||||
|  | 	char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); | ||||||
|  | 	const char *p; | ||||||
|  | 	char *new; | ||||||
|  | 	int len = strlen(s) + 1; | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s); | ||||||
|  | 	if (p) | ||||||
|  | 		/* found it */ | ||||||
|  | 		return (p - strtab); | ||||||
|  |  | ||||||
|  | 	new = strtab + fdt_size_dt_strings(fdt); | ||||||
|  | 	err = _fdt_splice_string(fdt, len); | ||||||
|  | 	if (err) | ||||||
|  | 		return err; | ||||||
|  |  | ||||||
|  | 	memcpy(new, s, len); | ||||||
|  | 	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_CHECK_HEADER(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_CHECK_HEADER(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 len, struct fdt_property **prop) | ||||||
|  | { | ||||||
|  | 	int oldlen; | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	*prop = fdt_get_property_w(fdt, nodeoffset, name, &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 len, struct fdt_property **prop) | ||||||
|  | { | ||||||
|  | 	int proplen; | ||||||
|  | 	int nextoffset; | ||||||
|  | 	int namestroff; | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) | ||||||
|  | 		return nextoffset; | ||||||
|  |  | ||||||
|  | 	namestroff = _fdt_find_add_string(fdt, name); | ||||||
|  | 	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) | ||||||
|  | 		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_CHECK_HEADER(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(void *fdt, int nodeoffset, const char *name, | ||||||
|  | 		const void *val, int len) | ||||||
|  | { | ||||||
|  | 	struct fdt_property *prop; | ||||||
|  | 	int err; | ||||||
|  |  | ||||||
|  | 	FDT_RW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop); | ||||||
|  | 	if (err == -FDT_ERR_NOTFOUND) | ||||||
|  | 		err = _fdt_add_property(fdt, nodeoffset, name, len, &prop); | ||||||
|  | 	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_CHECK_HEADER(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, 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_CHECK_HEADER(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_CHECK_HEADER(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 */ | ||||||
|  | 	fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ | ||||||
|  | 	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_CHECK_HEADER(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 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), | ||||||
|  | 		fdt_size_dt_strings(old)); | ||||||
|  | 	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_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) | ||||||
|  | 		* sizeof(struct fdt_reserve_entry); | ||||||
|  |  | ||||||
|  | 	if (fdt_version(fdt) >= 17) { | ||||||
|  | 		struct_size = fdt_size_dt_struct(fdt); | ||||||
|  | 	} else { | ||||||
|  | 		struct_size = 0; | ||||||
|  | 		while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) | ||||||
|  | 			; | ||||||
|  | 		if (struct_size < 0) | ||||||
|  | 			return struct_size; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!_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); | ||||||
|  | 	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) | ||||||
|  | { | ||||||
|  | 	int mem_rsv_size; | ||||||
|  |  | ||||||
|  | 	FDT_RW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) | ||||||
|  | 		* sizeof(struct fdt_reserve_entry); | ||||||
|  | 	_fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); | ||||||
|  | 	fdt_set_totalsize(fdt, _fdt_data_size(fdt)); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										102
									
								
								scripts/dtc/libfdt/fdt_strerror.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								scripts/dtc/libfdt/fdt_strerror.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include "libfdt_env.h" | ||||||
|  |  | ||||||
|  | #include <fdt.h> | ||||||
|  | #include <libfdt.h> | ||||||
|  |  | ||||||
|  | #include "libfdt_internal.h" | ||||||
|  |  | ||||||
|  | struct fdt_errtabent { | ||||||
|  | 	const char *str; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define FDT_ERRTABENT(val) \ | ||||||
|  | 	[(val)] = { .str = #val, } | ||||||
|  |  | ||||||
|  | static struct fdt_errtabent fdt_errtable[] = { | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_NOTFOUND), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_EXISTS), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_NOSPACE), | ||||||
|  |  | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADOFFSET), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADPATH), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADPHANDLE), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADSTATE), | ||||||
|  |  | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_TRUNCATED), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADMAGIC), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADVERSION), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADLAYOUT), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_INTERNAL), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADNCELLS), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADVALUE), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_BADOVERLAY), | ||||||
|  | 	FDT_ERRTABENT(FDT_ERR_NOPHANDLES), | ||||||
|  | }; | ||||||
|  | #define FDT_ERRTABSIZE	(sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) | ||||||
|  |  | ||||||
|  | const char *fdt_strerror(int errval) | ||||||
|  | { | ||||||
|  | 	if (errval > 0) | ||||||
|  | 		return "<valid offset/length>"; | ||||||
|  | 	else if (errval == 0) | ||||||
|  | 		return "<no error>"; | ||||||
|  | 	else if (errval > -FDT_ERRTABSIZE) { | ||||||
|  | 		const char *s = fdt_errtable[-errval].str; | ||||||
|  |  | ||||||
|  | 		if (s) | ||||||
|  | 			return s; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return "<unknown error>"; | ||||||
|  | } | ||||||
							
								
								
									
										288
									
								
								scripts/dtc/libfdt/fdt_sw.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								scripts/dtc/libfdt/fdt_sw.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,288 @@ | |||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include "libfdt_env.h" | ||||||
|  |  | ||||||
|  | #include <fdt.h> | ||||||
|  | #include <libfdt.h> | ||||||
|  |  | ||||||
|  | #include "libfdt_internal.h" | ||||||
|  |  | ||||||
|  | static int _fdt_sw_check_header(void *fdt) | ||||||
|  | { | ||||||
|  | 	if (fdt_magic(fdt) != FDT_SW_MAGIC) | ||||||
|  | 		return -FDT_ERR_BADMAGIC; | ||||||
|  | 	/* FIXME: should check more details about the header state */ | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define FDT_SW_CHECK_HEADER(fdt) \ | ||||||
|  | 	{ \ | ||||||
|  | 		int err; \ | ||||||
|  | 		if ((err = _fdt_sw_check_header(fdt)) != 0) \ | ||||||
|  | 			return err; \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | static void *_fdt_grab_space(void *fdt, size_t len) | ||||||
|  | { | ||||||
|  | 	int offset = fdt_size_dt_struct(fdt); | ||||||
|  | 	int spaceleft; | ||||||
|  |  | ||||||
|  | 	spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) | ||||||
|  | 		- fdt_size_dt_strings(fdt); | ||||||
|  |  | ||||||
|  | 	if ((offset + len < offset) || (offset + len > spaceleft)) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	fdt_set_size_dt_struct(fdt, offset + len); | ||||||
|  | 	return _fdt_offset_ptr_w(fdt, offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_create(void *buf, int bufsize) | ||||||
|  | { | ||||||
|  | 	void *fdt = buf; | ||||||
|  |  | ||||||
|  | 	if (bufsize < sizeof(struct fdt_header)) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	memset(buf, 0, bufsize); | ||||||
|  |  | ||||||
|  | 	fdt_set_magic(fdt, FDT_SW_MAGIC); | ||||||
|  | 	fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); | ||||||
|  | 	fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); | ||||||
|  | 	fdt_set_totalsize(fdt,  bufsize); | ||||||
|  |  | ||||||
|  | 	fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), | ||||||
|  | 					      sizeof(struct fdt_reserve_entry))); | ||||||
|  | 	fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); | ||||||
|  | 	fdt_set_off_dt_strings(fdt, bufsize); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_resize(void *fdt, void *buf, int bufsize) | ||||||
|  | { | ||||||
|  | 	size_t headsize, tailsize; | ||||||
|  | 	char *oldtail, *newtail; | ||||||
|  |  | ||||||
|  | 	FDT_SW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	headsize = fdt_off_dt_struct(fdt); | ||||||
|  | 	tailsize = fdt_size_dt_strings(fdt); | ||||||
|  |  | ||||||
|  | 	if ((headsize + tailsize) > bufsize) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; | ||||||
|  | 	newtail = (char *)buf + bufsize - tailsize; | ||||||
|  |  | ||||||
|  | 	/* Two cases to avoid clobbering data if the old and new | ||||||
|  | 	 * buffers partially overlap */ | ||||||
|  | 	if (buf <= fdt) { | ||||||
|  | 		memmove(buf, fdt, headsize); | ||||||
|  | 		memmove(newtail, oldtail, tailsize); | ||||||
|  | 	} else { | ||||||
|  | 		memmove(newtail, oldtail, tailsize); | ||||||
|  | 		memmove(buf, fdt, headsize); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fdt_set_off_dt_strings(buf, bufsize); | ||||||
|  | 	fdt_set_totalsize(buf, bufsize); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) | ||||||
|  | { | ||||||
|  | 	struct fdt_reserve_entry *re; | ||||||
|  | 	int offset; | ||||||
|  |  | ||||||
|  | 	FDT_SW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	if (fdt_size_dt_struct(fdt)) | ||||||
|  | 		return -FDT_ERR_BADSTATE; | ||||||
|  |  | ||||||
|  | 	offset = fdt_off_dt_struct(fdt); | ||||||
|  | 	if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	re = (struct fdt_reserve_entry *)((char *)fdt + offset); | ||||||
|  | 	re->address = cpu_to_fdt64(addr); | ||||||
|  | 	re->size = cpu_to_fdt64(size); | ||||||
|  |  | ||||||
|  | 	fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_finish_reservemap(void *fdt) | ||||||
|  | { | ||||||
|  | 	return fdt_add_reservemap_entry(fdt, 0, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_begin_node(void *fdt, const char *name) | ||||||
|  | { | ||||||
|  | 	struct fdt_node_header *nh; | ||||||
|  | 	int namelen = strlen(name) + 1; | ||||||
|  |  | ||||||
|  | 	FDT_SW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); | ||||||
|  | 	if (! nh) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); | ||||||
|  | 	memcpy(nh->name, name, namelen); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_end_node(void *fdt) | ||||||
|  | { | ||||||
|  | 	fdt32_t *en; | ||||||
|  |  | ||||||
|  | 	FDT_SW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	en = _fdt_grab_space(fdt, FDT_TAGSIZE); | ||||||
|  | 	if (! en) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	*en = cpu_to_fdt32(FDT_END_NODE); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int _fdt_find_add_string(void *fdt, const char *s) | ||||||
|  | { | ||||||
|  | 	char *strtab = (char *)fdt + fdt_totalsize(fdt); | ||||||
|  | 	const char *p; | ||||||
|  | 	int strtabsize = fdt_size_dt_strings(fdt); | ||||||
|  | 	int len = strlen(s) + 1; | ||||||
|  | 	int struct_top, offset; | ||||||
|  |  | ||||||
|  | 	p = _fdt_find_string(strtab - strtabsize, strtabsize, s); | ||||||
|  | 	if (p) | ||||||
|  | 		return p - strtab; | ||||||
|  |  | ||||||
|  | 	/* Add it */ | ||||||
|  | 	offset = -strtabsize - len; | ||||||
|  | 	struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); | ||||||
|  | 	if (fdt_totalsize(fdt) + offset < struct_top) | ||||||
|  | 		return 0; /* no more room :( */ | ||||||
|  |  | ||||||
|  | 	memcpy(strtab + offset, s, len); | ||||||
|  | 	fdt_set_size_dt_strings(fdt, strtabsize + len); | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_property(void *fdt, const char *name, const void *val, int len) | ||||||
|  | { | ||||||
|  | 	struct fdt_property *prop; | ||||||
|  | 	int nameoff; | ||||||
|  |  | ||||||
|  | 	FDT_SW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	nameoff = _fdt_find_add_string(fdt, name); | ||||||
|  | 	if (nameoff == 0) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); | ||||||
|  | 	if (! prop) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	prop->tag = cpu_to_fdt32(FDT_PROP); | ||||||
|  | 	prop->nameoff = cpu_to_fdt32(nameoff); | ||||||
|  | 	prop->len = cpu_to_fdt32(len); | ||||||
|  | 	memcpy(prop->data, val, len); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_finish(void *fdt) | ||||||
|  | { | ||||||
|  | 	char *p = (char *)fdt; | ||||||
|  | 	fdt32_t *end; | ||||||
|  | 	int oldstroffset, newstroffset; | ||||||
|  | 	uint32_t tag; | ||||||
|  | 	int offset, nextoffset; | ||||||
|  |  | ||||||
|  | 	FDT_SW_CHECK_HEADER(fdt); | ||||||
|  |  | ||||||
|  | 	/* Add terminator */ | ||||||
|  | 	end = _fdt_grab_space(fdt, sizeof(*end)); | ||||||
|  | 	if (! end) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  | 	*end = cpu_to_fdt32(FDT_END); | ||||||
|  |  | ||||||
|  | 	/* Relocate the string table */ | ||||||
|  | 	oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); | ||||||
|  | 	newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); | ||||||
|  | 	memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); | ||||||
|  | 	fdt_set_off_dt_strings(fdt, newstroffset); | ||||||
|  |  | ||||||
|  | 	/* Walk the structure, correcting string offsets */ | ||||||
|  | 	offset = 0; | ||||||
|  | 	while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { | ||||||
|  | 		if (tag == FDT_PROP) { | ||||||
|  | 			struct fdt_property *prop = | ||||||
|  | 				_fdt_offset_ptr_w(fdt, offset); | ||||||
|  | 			int nameoff; | ||||||
|  |  | ||||||
|  | 			nameoff = fdt32_to_cpu(prop->nameoff); | ||||||
|  | 			nameoff += fdt_size_dt_strings(fdt); | ||||||
|  | 			prop->nameoff = cpu_to_fdt32(nameoff); | ||||||
|  | 		} | ||||||
|  | 		offset = nextoffset; | ||||||
|  | 	} | ||||||
|  | 	if (nextoffset < 0) | ||||||
|  | 		return nextoffset; | ||||||
|  |  | ||||||
|  | 	/* Finally, adjust the header */ | ||||||
|  | 	fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); | ||||||
|  | 	fdt_set_magic(fdt, FDT_MAGIC); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										139
									
								
								scripts/dtc/libfdt/fdt_wip.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								scripts/dtc/libfdt/fdt_wip.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | |||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include "libfdt_env.h" | ||||||
|  |  | ||||||
|  | #include <fdt.h> | ||||||
|  | #include <libfdt.h> | ||||||
|  |  | ||||||
|  | #include "libfdt_internal.h" | ||||||
|  |  | ||||||
|  | int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, | ||||||
|  | 					const char *name, int namelen, | ||||||
|  | 					uint32_t idx, const void *val, | ||||||
|  | 					int len) | ||||||
|  | { | ||||||
|  | 	void *propval; | ||||||
|  | 	int proplen; | ||||||
|  |  | ||||||
|  | 	propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, | ||||||
|  | 					&proplen); | ||||||
|  | 	if (!propval) | ||||||
|  | 		return proplen; | ||||||
|  |  | ||||||
|  | 	if (proplen < (len + idx)) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	memcpy((char *)propval + idx, val, len); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, | ||||||
|  | 			const void *val, int len) | ||||||
|  | { | ||||||
|  | 	const void *propval; | ||||||
|  | 	int proplen; | ||||||
|  |  | ||||||
|  | 	propval = fdt_getprop(fdt, nodeoffset, name, &proplen); | ||||||
|  | 	if (! propval) | ||||||
|  | 		return proplen; | ||||||
|  |  | ||||||
|  | 	if (proplen != len) | ||||||
|  | 		return -FDT_ERR_NOSPACE; | ||||||
|  |  | ||||||
|  | 	return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, | ||||||
|  | 						   strlen(name), 0, | ||||||
|  | 						   val, len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void _fdt_nop_region(void *start, int len) | ||||||
|  | { | ||||||
|  | 	fdt32_t *p; | ||||||
|  |  | ||||||
|  | 	for (p = start; (char *)p < ((char *)start + len); p++) | ||||||
|  | 		*p = cpu_to_fdt32(FDT_NOP); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_nop_property(void *fdt, int nodeoffset, const char *name) | ||||||
|  | { | ||||||
|  | 	struct fdt_property *prop; | ||||||
|  | 	int len; | ||||||
|  |  | ||||||
|  | 	prop = fdt_get_property_w(fdt, nodeoffset, name, &len); | ||||||
|  | 	if (! prop) | ||||||
|  | 		return len; | ||||||
|  |  | ||||||
|  | 	_fdt_nop_region(prop, len + sizeof(*prop)); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int _fdt_node_end_offset(void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	int depth = 0; | ||||||
|  |  | ||||||
|  | 	while ((offset >= 0) && (depth >= 0)) | ||||||
|  | 		offset = fdt_next_node(fdt, offset, &depth); | ||||||
|  |  | ||||||
|  | 	return offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int fdt_nop_node(void *fdt, int nodeoffset) | ||||||
|  | { | ||||||
|  | 	int endoffset; | ||||||
|  |  | ||||||
|  | 	endoffset = _fdt_node_end_offset(fdt, nodeoffset); | ||||||
|  | 	if (endoffset < 0) | ||||||
|  | 		return endoffset; | ||||||
|  |  | ||||||
|  | 	_fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), | ||||||
|  | 			endoffset - nodeoffset); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										1833
									
								
								scripts/dtc/libfdt/libfdt.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1833
									
								
								scripts/dtc/libfdt/libfdt.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										112
									
								
								scripts/dtc/libfdt/libfdt_env.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								scripts/dtc/libfdt/libfdt_env.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | |||||||
|  | #ifndef _LIBFDT_ENV_H | ||||||
|  | #define _LIBFDT_ENV_H | ||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * Copyright 2012 Kim Phillips, Freescale Semiconductor. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #ifdef __CHECKER__ | ||||||
|  | #define __force __attribute__((force)) | ||||||
|  | #define __bitwise __attribute__((bitwise)) | ||||||
|  | #else | ||||||
|  | #define __force | ||||||
|  | #define __bitwise | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | typedef uint16_t __bitwise fdt16_t; | ||||||
|  | typedef uint32_t __bitwise fdt32_t; | ||||||
|  | typedef uint64_t __bitwise fdt64_t; | ||||||
|  |  | ||||||
|  | #define EXTRACT_BYTE(x, n)	((unsigned long long)((uint8_t *)&x)[n]) | ||||||
|  | #define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) | ||||||
|  | #define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ | ||||||
|  | 			 (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) | ||||||
|  | #define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ | ||||||
|  | 			 (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ | ||||||
|  | 			 (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ | ||||||
|  | 			 (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) | ||||||
|  |  | ||||||
|  | static inline uint16_t fdt16_to_cpu(fdt16_t x) | ||||||
|  | { | ||||||
|  | 	return (__force uint16_t)CPU_TO_FDT16(x); | ||||||
|  | } | ||||||
|  | static inline fdt16_t cpu_to_fdt16(uint16_t x) | ||||||
|  | { | ||||||
|  | 	return (__force fdt16_t)CPU_TO_FDT16(x); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline uint32_t fdt32_to_cpu(fdt32_t x) | ||||||
|  | { | ||||||
|  | 	return (__force uint32_t)CPU_TO_FDT32(x); | ||||||
|  | } | ||||||
|  | static inline fdt32_t cpu_to_fdt32(uint32_t x) | ||||||
|  | { | ||||||
|  | 	return (__force fdt32_t)CPU_TO_FDT32(x); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline uint64_t fdt64_to_cpu(fdt64_t x) | ||||||
|  | { | ||||||
|  | 	return (__force uint64_t)CPU_TO_FDT64(x); | ||||||
|  | } | ||||||
|  | static inline fdt64_t cpu_to_fdt64(uint64_t x) | ||||||
|  | { | ||||||
|  | 	return (__force fdt64_t)CPU_TO_FDT64(x); | ||||||
|  | } | ||||||
|  | #undef CPU_TO_FDT64 | ||||||
|  | #undef CPU_TO_FDT32 | ||||||
|  | #undef CPU_TO_FDT16 | ||||||
|  | #undef EXTRACT_BYTE | ||||||
|  |  | ||||||
|  | #endif /* _LIBFDT_ENV_H */ | ||||||
							
								
								
									
										95
									
								
								scripts/dtc/libfdt/libfdt_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								scripts/dtc/libfdt/libfdt_internal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | |||||||
|  | #ifndef _LIBFDT_INTERNAL_H | ||||||
|  | #define _LIBFDT_INTERNAL_H | ||||||
|  | /* | ||||||
|  |  * libfdt - Flat Device Tree manipulation | ||||||
|  |  * Copyright (C) 2006 David Gibson, IBM Corporation. | ||||||
|  |  * | ||||||
|  |  * libfdt is dual licensed: you can use it either under the terms of | ||||||
|  |  * the GPL, or the BSD license, at your option. | ||||||
|  |  * | ||||||
|  |  *  a) This library is free software; you can redistribute it and/or | ||||||
|  |  *     modify it under the terms of the GNU General Public License as | ||||||
|  |  *     published by the Free Software Foundation; either version 2 of the | ||||||
|  |  *     License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *     This library is distributed in the hope that it will be useful, | ||||||
|  |  *     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  *     GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *     You should have received a copy of the GNU General Public | ||||||
|  |  *     License along with this library; if not, write to the Free | ||||||
|  |  *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | ||||||
|  |  *     MA 02110-1301 USA | ||||||
|  |  * | ||||||
|  |  * Alternatively, | ||||||
|  |  * | ||||||
|  |  *  b) Redistribution and use in source and binary forms, with or | ||||||
|  |  *     without modification, are permitted provided that the following | ||||||
|  |  *     conditions are met: | ||||||
|  |  * | ||||||
|  |  *     1. Redistributions of source code must retain the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer. | ||||||
|  |  *     2. Redistributions in binary form must reproduce the above | ||||||
|  |  *        copyright notice, this list of conditions and the following | ||||||
|  |  *        disclaimer in the documentation and/or other materials | ||||||
|  |  *        provided with the distribution. | ||||||
|  |  * | ||||||
|  |  *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||||||
|  |  *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||||||
|  |  *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||||||
|  |  *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | ||||||
|  |  *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  |  *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |  *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||||
|  |  *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||
|  |  *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||||||
|  |  *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | ||||||
|  |  *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include <fdt.h> | ||||||
|  |  | ||||||
|  | #define FDT_ALIGN(x, a)		(((x) + (a) - 1) & ~((a) - 1)) | ||||||
|  | #define FDT_TAGALIGN(x)		(FDT_ALIGN((x), FDT_TAGSIZE)) | ||||||
|  |  | ||||||
|  | #define FDT_CHECK_HEADER(fdt) \ | ||||||
|  | 	{ \ | ||||||
|  | 		int __err; \ | ||||||
|  | 		if ((__err = fdt_check_header(fdt)) != 0) \ | ||||||
|  | 			return __err; \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | int _fdt_check_node_offset(const void *fdt, int offset); | ||||||
|  | int _fdt_check_prop_offset(const void *fdt, int offset); | ||||||
|  | const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); | ||||||
|  | int _fdt_node_end_offset(void *fdt, int nodeoffset); | ||||||
|  |  | ||||||
|  | static inline const void *_fdt_offset_ptr(const void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline void *_fdt_offset_ptr_w(void *fdt, int offset) | ||||||
|  | { | ||||||
|  | 	return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) | ||||||
|  | { | ||||||
|  | 	const struct fdt_reserve_entry *rsv_table = | ||||||
|  | 		(const struct fdt_reserve_entry *) | ||||||
|  | 		((const char *)fdt + fdt_off_mem_rsvmap(fdt)); | ||||||
|  |  | ||||||
|  | 	return rsv_table + n; | ||||||
|  | } | ||||||
|  | static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) | ||||||
|  | { | ||||||
|  | 	return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define FDT_SW_MAGIC		(~FDT_MAGIC) | ||||||
|  |  | ||||||
|  | #endif /* _LIBFDT_INTERNAL_H */ | ||||||
							
								
								
									
										980
									
								
								scripts/dtc/livetree.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										980
									
								
								scripts/dtc/livetree.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,980 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Tree building functions | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | void add_label(struct label **labels, char *label) | ||||||
|  | { | ||||||
|  | 	struct label *new; | ||||||
|  |  | ||||||
|  | 	/* Make sure the label isn't already there */ | ||||||
|  | 	for_each_label_withdel(*labels, new) | ||||||
|  | 		if (streq(new->label, label)) { | ||||||
|  | 			new->deleted = 0; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	new = xmalloc(sizeof(*new)); | ||||||
|  | 	memset(new, 0, sizeof(*new)); | ||||||
|  | 	new->label = label; | ||||||
|  | 	new->next = *labels; | ||||||
|  | 	*labels = new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void delete_labels(struct label **labels) | ||||||
|  | { | ||||||
|  | 	struct label *label; | ||||||
|  |  | ||||||
|  | 	for_each_label(*labels, label) | ||||||
|  | 		label->deleted = 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct property *build_property(char *name, struct data val) | ||||||
|  | { | ||||||
|  | 	struct property *new = xmalloc(sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	memset(new, 0, sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	new->name = name; | ||||||
|  | 	new->val = val; | ||||||
|  |  | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct property *build_property_delete(char *name) | ||||||
|  | { | ||||||
|  | 	struct property *new = xmalloc(sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	memset(new, 0, sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	new->name = name; | ||||||
|  | 	new->deleted = 1; | ||||||
|  |  | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct property *chain_property(struct property *first, struct property *list) | ||||||
|  | { | ||||||
|  | 	assert(first->next == NULL); | ||||||
|  |  | ||||||
|  | 	first->next = list; | ||||||
|  | 	return first; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct property *reverse_properties(struct property *first) | ||||||
|  | { | ||||||
|  | 	struct property *p = first; | ||||||
|  | 	struct property *head = NULL; | ||||||
|  | 	struct property *next; | ||||||
|  |  | ||||||
|  | 	while (p) { | ||||||
|  | 		next = p->next; | ||||||
|  | 		p->next = head; | ||||||
|  | 		head = p; | ||||||
|  | 		p = next; | ||||||
|  | 	} | ||||||
|  | 	return head; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *build_node(struct property *proplist, struct node *children) | ||||||
|  | { | ||||||
|  | 	struct node *new = xmalloc(sizeof(*new)); | ||||||
|  | 	struct node *child; | ||||||
|  |  | ||||||
|  | 	memset(new, 0, sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	new->proplist = reverse_properties(proplist); | ||||||
|  | 	new->children = children; | ||||||
|  |  | ||||||
|  | 	for_each_child(new, child) { | ||||||
|  | 		child->parent = new; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *build_node_delete(void) | ||||||
|  | { | ||||||
|  | 	struct node *new = xmalloc(sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	memset(new, 0, sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	new->deleted = 1; | ||||||
|  |  | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *name_node(struct node *node, char *name) | ||||||
|  | { | ||||||
|  | 	assert(node->name == NULL); | ||||||
|  |  | ||||||
|  | 	node->name = name; | ||||||
|  |  | ||||||
|  | 	return node; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *merge_nodes(struct node *old_node, struct node *new_node) | ||||||
|  | { | ||||||
|  | 	struct property *new_prop, *old_prop; | ||||||
|  | 	struct node *new_child, *old_child; | ||||||
|  | 	struct label *l; | ||||||
|  |  | ||||||
|  | 	old_node->deleted = 0; | ||||||
|  |  | ||||||
|  | 	/* Add new node labels to old node */ | ||||||
|  | 	for_each_label_withdel(new_node->labels, l) | ||||||
|  | 		add_label(&old_node->labels, l->label); | ||||||
|  |  | ||||||
|  | 	/* Move properties from the new node to the old node.  If there | ||||||
|  | 	 * is a collision, replace the old value with the new */ | ||||||
|  | 	while (new_node->proplist) { | ||||||
|  | 		/* Pop the property off the list */ | ||||||
|  | 		new_prop = new_node->proplist; | ||||||
|  | 		new_node->proplist = new_prop->next; | ||||||
|  | 		new_prop->next = NULL; | ||||||
|  |  | ||||||
|  | 		if (new_prop->deleted) { | ||||||
|  | 			delete_property_by_name(old_node, new_prop->name); | ||||||
|  | 			free(new_prop); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Look for a collision, set new value if there is */ | ||||||
|  | 		for_each_property_withdel(old_node, old_prop) { | ||||||
|  | 			if (streq(old_prop->name, new_prop->name)) { | ||||||
|  | 				/* Add new labels to old property */ | ||||||
|  | 				for_each_label_withdel(new_prop->labels, l) | ||||||
|  | 					add_label(&old_prop->labels, l->label); | ||||||
|  |  | ||||||
|  | 				old_prop->val = new_prop->val; | ||||||
|  | 				old_prop->deleted = 0; | ||||||
|  | 				free(new_prop); | ||||||
|  | 				new_prop = NULL; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* if no collision occurred, add property to the old node. */ | ||||||
|  | 		if (new_prop) | ||||||
|  | 			add_property(old_node, new_prop); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Move the override child nodes into the primary node.  If | ||||||
|  | 	 * there is a collision, then merge the nodes. */ | ||||||
|  | 	while (new_node->children) { | ||||||
|  | 		/* Pop the child node off the list */ | ||||||
|  | 		new_child = new_node->children; | ||||||
|  | 		new_node->children = new_child->next_sibling; | ||||||
|  | 		new_child->parent = NULL; | ||||||
|  | 		new_child->next_sibling = NULL; | ||||||
|  |  | ||||||
|  | 		if (new_child->deleted) { | ||||||
|  | 			delete_node_by_name(old_node, new_child->name); | ||||||
|  | 			free(new_child); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Search for a collision.  Merge if there is */ | ||||||
|  | 		for_each_child_withdel(old_node, old_child) { | ||||||
|  | 			if (streq(old_child->name, new_child->name)) { | ||||||
|  | 				merge_nodes(old_child, new_child); | ||||||
|  | 				new_child = NULL; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* if no collision occurred, add child to the old node. */ | ||||||
|  | 		if (new_child) | ||||||
|  | 			add_child(old_node, new_child); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* The new node contents are now merged into the old node.  Free | ||||||
|  | 	 * the new node. */ | ||||||
|  | 	free(new_node); | ||||||
|  |  | ||||||
|  | 	return old_node; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *chain_node(struct node *first, struct node *list) | ||||||
|  | { | ||||||
|  | 	assert(first->next_sibling == NULL); | ||||||
|  |  | ||||||
|  | 	first->next_sibling = list; | ||||||
|  | 	return first; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void add_property(struct node *node, struct property *prop) | ||||||
|  | { | ||||||
|  | 	struct property **p; | ||||||
|  |  | ||||||
|  | 	prop->next = NULL; | ||||||
|  |  | ||||||
|  | 	p = &node->proplist; | ||||||
|  | 	while (*p) | ||||||
|  | 		p = &((*p)->next); | ||||||
|  |  | ||||||
|  | 	*p = prop; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void delete_property_by_name(struct node *node, char *name) | ||||||
|  | { | ||||||
|  | 	struct property *prop = node->proplist; | ||||||
|  |  | ||||||
|  | 	while (prop) { | ||||||
|  | 		if (streq(prop->name, name)) { | ||||||
|  | 			delete_property(prop); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		prop = prop->next; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void delete_property(struct property *prop) | ||||||
|  | { | ||||||
|  | 	prop->deleted = 1; | ||||||
|  | 	delete_labels(&prop->labels); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void add_child(struct node *parent, struct node *child) | ||||||
|  | { | ||||||
|  | 	struct node **p; | ||||||
|  |  | ||||||
|  | 	child->next_sibling = NULL; | ||||||
|  | 	child->parent = parent; | ||||||
|  |  | ||||||
|  | 	p = &parent->children; | ||||||
|  | 	while (*p) | ||||||
|  | 		p = &((*p)->next_sibling); | ||||||
|  |  | ||||||
|  | 	*p = child; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void delete_node_by_name(struct node *parent, char *name) | ||||||
|  | { | ||||||
|  | 	struct node *node = parent->children; | ||||||
|  |  | ||||||
|  | 	while (node) { | ||||||
|  | 		if (streq(node->name, name)) { | ||||||
|  | 			delete_node(node); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		node = node->next_sibling; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void delete_node(struct node *node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct node *child; | ||||||
|  |  | ||||||
|  | 	node->deleted = 1; | ||||||
|  | 	for_each_child(node, child) | ||||||
|  | 		delete_node(child); | ||||||
|  | 	for_each_property(node, prop) | ||||||
|  | 		delete_property(prop); | ||||||
|  | 	delete_labels(&node->labels); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void append_to_property(struct node *node, | ||||||
|  | 				    char *name, const void *data, int len) | ||||||
|  | { | ||||||
|  | 	struct data d; | ||||||
|  | 	struct property *p; | ||||||
|  |  | ||||||
|  | 	p = get_property(node, name); | ||||||
|  | 	if (p) { | ||||||
|  | 		d = data_append_data(p->val, data, len); | ||||||
|  | 		p->val = d; | ||||||
|  | 	} else { | ||||||
|  | 		d = data_append_data(empty_data, data, len); | ||||||
|  | 		p = build_property(name, d); | ||||||
|  | 		add_property(node, p); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct reserve_info *build_reserve_entry(uint64_t address, uint64_t size) | ||||||
|  | { | ||||||
|  | 	struct reserve_info *new = xmalloc(sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	memset(new, 0, sizeof(*new)); | ||||||
|  |  | ||||||
|  | 	new->re.address = address; | ||||||
|  | 	new->re.size = size; | ||||||
|  |  | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct reserve_info *chain_reserve_entry(struct reserve_info *first, | ||||||
|  | 					struct reserve_info *list) | ||||||
|  | { | ||||||
|  | 	assert(first->next == NULL); | ||||||
|  |  | ||||||
|  | 	first->next = list; | ||||||
|  | 	return first; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct reserve_info *add_reserve_entry(struct reserve_info *list, | ||||||
|  | 				      struct reserve_info *new) | ||||||
|  | { | ||||||
|  | 	struct reserve_info *last; | ||||||
|  |  | ||||||
|  | 	new->next = NULL; | ||||||
|  |  | ||||||
|  | 	if (! list) | ||||||
|  | 		return new; | ||||||
|  |  | ||||||
|  | 	for (last = list; last->next; last = last->next) | ||||||
|  | 		; | ||||||
|  |  | ||||||
|  | 	last->next = new; | ||||||
|  |  | ||||||
|  | 	return list; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct dt_info *build_dt_info(unsigned int dtsflags, | ||||||
|  | 			      struct reserve_info *reservelist, | ||||||
|  | 			      struct node *tree, uint32_t boot_cpuid_phys) | ||||||
|  | { | ||||||
|  | 	struct dt_info *dti; | ||||||
|  |  | ||||||
|  | 	dti = xmalloc(sizeof(*dti)); | ||||||
|  | 	dti->dtsflags = dtsflags; | ||||||
|  | 	dti->reservelist = reservelist; | ||||||
|  | 	dti->dt = tree; | ||||||
|  | 	dti->boot_cpuid_phys = boot_cpuid_phys; | ||||||
|  |  | ||||||
|  | 	return dti; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Tree accessor functions | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | const char *get_unitname(struct node *node) | ||||||
|  | { | ||||||
|  | 	if (node->name[node->basenamelen] == '\0') | ||||||
|  | 		return ""; | ||||||
|  | 	else | ||||||
|  | 		return node->name + node->basenamelen + 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct property *get_property(struct node *node, const char *propname) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) | ||||||
|  | 		if (streq(prop->name, propname)) | ||||||
|  | 			return prop; | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cell_t propval_cell(struct property *prop) | ||||||
|  | { | ||||||
|  | 	assert(prop->val.len == sizeof(cell_t)); | ||||||
|  | 	return fdt32_to_cpu(*((cell_t *)prop->val.val)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct property *get_property_by_label(struct node *tree, const char *label, | ||||||
|  | 				       struct node **node) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct node *c; | ||||||
|  |  | ||||||
|  | 	*node = tree; | ||||||
|  |  | ||||||
|  | 	for_each_property(tree, prop) { | ||||||
|  | 		struct label *l; | ||||||
|  |  | ||||||
|  | 		for_each_label(prop->labels, l) | ||||||
|  | 			if (streq(l->label, label)) | ||||||
|  | 				return prop; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(tree, c) { | ||||||
|  | 		prop = get_property_by_label(c, label, node); | ||||||
|  | 		if (prop) | ||||||
|  | 			return prop; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	*node = NULL; | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct marker *get_marker_label(struct node *tree, const char *label, | ||||||
|  | 				struct node **node, struct property **prop) | ||||||
|  | { | ||||||
|  | 	struct marker *m; | ||||||
|  | 	struct property *p; | ||||||
|  | 	struct node *c; | ||||||
|  |  | ||||||
|  | 	*node = tree; | ||||||
|  |  | ||||||
|  | 	for_each_property(tree, p) { | ||||||
|  | 		*prop = p; | ||||||
|  | 		m = p->val.markers; | ||||||
|  | 		for_each_marker_of_type(m, LABEL) | ||||||
|  | 			if (streq(m->ref, label)) | ||||||
|  | 				return m; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(tree, c) { | ||||||
|  | 		m = get_marker_label(c, label, node, prop); | ||||||
|  | 		if (m) | ||||||
|  | 			return m; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	*prop = NULL; | ||||||
|  | 	*node = NULL; | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *get_subnode(struct node *node, const char *nodename) | ||||||
|  | { | ||||||
|  | 	struct node *child; | ||||||
|  |  | ||||||
|  | 	for_each_child(node, child) | ||||||
|  | 		if (streq(child->name, nodename)) | ||||||
|  | 			return child; | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *get_node_by_path(struct node *tree, const char *path) | ||||||
|  | { | ||||||
|  | 	const char *p; | ||||||
|  | 	struct node *child; | ||||||
|  |  | ||||||
|  | 	if (!path || ! (*path)) { | ||||||
|  | 		if (tree->deleted) | ||||||
|  | 			return NULL; | ||||||
|  | 		return tree; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	while (path[0] == '/') | ||||||
|  | 		path++; | ||||||
|  |  | ||||||
|  | 	p = strchr(path, '/'); | ||||||
|  |  | ||||||
|  | 	for_each_child(tree, child) { | ||||||
|  | 		if (p && strneq(path, child->name, p-path)) | ||||||
|  | 			return get_node_by_path(child, p+1); | ||||||
|  | 		else if (!p && streq(path, child->name)) | ||||||
|  | 			return child; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *get_node_by_label(struct node *tree, const char *label) | ||||||
|  | { | ||||||
|  | 	struct node *child, *node; | ||||||
|  | 	struct label *l; | ||||||
|  |  | ||||||
|  | 	assert(label && (strlen(label) > 0)); | ||||||
|  |  | ||||||
|  | 	for_each_label(tree->labels, l) | ||||||
|  | 		if (streq(l->label, label)) | ||||||
|  | 			return tree; | ||||||
|  |  | ||||||
|  | 	for_each_child(tree, child) { | ||||||
|  | 		node = get_node_by_label(child, label); | ||||||
|  | 		if (node) | ||||||
|  | 			return node; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *get_node_by_phandle(struct node *tree, cell_t phandle) | ||||||
|  | { | ||||||
|  | 	struct node *child, *node; | ||||||
|  |  | ||||||
|  | 	assert((phandle != 0) && (phandle != -1)); | ||||||
|  |  | ||||||
|  | 	if (tree->phandle == phandle) { | ||||||
|  | 		if (tree->deleted) | ||||||
|  | 			return NULL; | ||||||
|  | 		return tree; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(tree, child) { | ||||||
|  | 		node = get_node_by_phandle(child, phandle); | ||||||
|  | 		if (node) | ||||||
|  | 			return node; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct node *get_node_by_ref(struct node *tree, const char *ref) | ||||||
|  | { | ||||||
|  | 	if (streq(ref, "/")) | ||||||
|  | 		return tree; | ||||||
|  | 	else if (ref[0] == '/') | ||||||
|  | 		return get_node_by_path(tree, ref); | ||||||
|  | 	else | ||||||
|  | 		return get_node_by_label(tree, ref); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | cell_t get_node_phandle(struct node *root, struct node *node) | ||||||
|  | { | ||||||
|  | 	static cell_t phandle = 1; /* FIXME: ick, static local */ | ||||||
|  |  | ||||||
|  | 	if ((node->phandle != 0) && (node->phandle != -1)) | ||||||
|  | 		return node->phandle; | ||||||
|  |  | ||||||
|  | 	while (get_node_by_phandle(root, phandle)) | ||||||
|  | 		phandle++; | ||||||
|  |  | ||||||
|  | 	node->phandle = phandle; | ||||||
|  |  | ||||||
|  | 	if (!get_property(node, "linux,phandle") | ||||||
|  | 	    && (phandle_format & PHANDLE_LEGACY)) | ||||||
|  | 		add_property(node, | ||||||
|  | 			     build_property("linux,phandle", | ||||||
|  | 					    data_append_cell(empty_data, phandle))); | ||||||
|  |  | ||||||
|  | 	if (!get_property(node, "phandle") | ||||||
|  | 	    && (phandle_format & PHANDLE_EPAPR)) | ||||||
|  | 		add_property(node, | ||||||
|  | 			     build_property("phandle", | ||||||
|  | 					    data_append_cell(empty_data, phandle))); | ||||||
|  |  | ||||||
|  | 	/* If the node *does* have a phandle property, we must | ||||||
|  | 	 * be dealing with a self-referencing phandle, which will be | ||||||
|  | 	 * fixed up momentarily in the caller */ | ||||||
|  |  | ||||||
|  | 	return node->phandle; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint32_t guess_boot_cpuid(struct node *tree) | ||||||
|  | { | ||||||
|  | 	struct node *cpus, *bootcpu; | ||||||
|  | 	struct property *reg; | ||||||
|  |  | ||||||
|  | 	cpus = get_node_by_path(tree, "/cpus"); | ||||||
|  | 	if (!cpus) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	bootcpu = cpus->children; | ||||||
|  | 	if (!bootcpu) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	reg = get_property(bootcpu, "reg"); | ||||||
|  | 	if (!reg || (reg->val.len != sizeof(uint32_t))) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	/* FIXME: Sanity check node? */ | ||||||
|  |  | ||||||
|  | 	return propval_cell(reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmp_reserve_info(const void *ax, const void *bx) | ||||||
|  | { | ||||||
|  | 	const struct reserve_info *a, *b; | ||||||
|  |  | ||||||
|  | 	a = *((const struct reserve_info * const *)ax); | ||||||
|  | 	b = *((const struct reserve_info * const *)bx); | ||||||
|  |  | ||||||
|  | 	if (a->re.address < b->re.address) | ||||||
|  | 		return -1; | ||||||
|  | 	else if (a->re.address > b->re.address) | ||||||
|  | 		return 1; | ||||||
|  | 	else if (a->re.size < b->re.size) | ||||||
|  | 		return -1; | ||||||
|  | 	else if (a->re.size > b->re.size) | ||||||
|  | 		return 1; | ||||||
|  | 	else | ||||||
|  | 		return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sort_reserve_entries(struct dt_info *dti) | ||||||
|  | { | ||||||
|  | 	struct reserve_info *ri, **tbl; | ||||||
|  | 	int n = 0, i = 0; | ||||||
|  |  | ||||||
|  | 	for (ri = dti->reservelist; | ||||||
|  | 	     ri; | ||||||
|  | 	     ri = ri->next) | ||||||
|  | 		n++; | ||||||
|  |  | ||||||
|  | 	if (n == 0) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	tbl = xmalloc(n * sizeof(*tbl)); | ||||||
|  |  | ||||||
|  | 	for (ri = dti->reservelist; | ||||||
|  | 	     ri; | ||||||
|  | 	     ri = ri->next) | ||||||
|  | 		tbl[i++] = ri; | ||||||
|  |  | ||||||
|  | 	qsort(tbl, n, sizeof(*tbl), cmp_reserve_info); | ||||||
|  |  | ||||||
|  | 	dti->reservelist = tbl[0]; | ||||||
|  | 	for (i = 0; i < (n-1); i++) | ||||||
|  | 		tbl[i]->next = tbl[i+1]; | ||||||
|  | 	tbl[n-1]->next = NULL; | ||||||
|  |  | ||||||
|  | 	free(tbl); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmp_prop(const void *ax, const void *bx) | ||||||
|  | { | ||||||
|  | 	const struct property *a, *b; | ||||||
|  |  | ||||||
|  | 	a = *((const struct property * const *)ax); | ||||||
|  | 	b = *((const struct property * const *)bx); | ||||||
|  |  | ||||||
|  | 	return strcmp(a->name, b->name); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sort_properties(struct node *node) | ||||||
|  | { | ||||||
|  | 	int n = 0, i = 0; | ||||||
|  | 	struct property *prop, **tbl; | ||||||
|  |  | ||||||
|  | 	for_each_property_withdel(node, prop) | ||||||
|  | 		n++; | ||||||
|  |  | ||||||
|  | 	if (n == 0) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	tbl = xmalloc(n * sizeof(*tbl)); | ||||||
|  |  | ||||||
|  | 	for_each_property_withdel(node, prop) | ||||||
|  | 		tbl[i++] = prop; | ||||||
|  |  | ||||||
|  | 	qsort(tbl, n, sizeof(*tbl), cmp_prop); | ||||||
|  |  | ||||||
|  | 	node->proplist = tbl[0]; | ||||||
|  | 	for (i = 0; i < (n-1); i++) | ||||||
|  | 		tbl[i]->next = tbl[i+1]; | ||||||
|  | 	tbl[n-1]->next = NULL; | ||||||
|  |  | ||||||
|  | 	free(tbl); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmp_subnode(const void *ax, const void *bx) | ||||||
|  | { | ||||||
|  | 	const struct node *a, *b; | ||||||
|  |  | ||||||
|  | 	a = *((const struct node * const *)ax); | ||||||
|  | 	b = *((const struct node * const *)bx); | ||||||
|  |  | ||||||
|  | 	return strcmp(a->name, b->name); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sort_subnodes(struct node *node) | ||||||
|  | { | ||||||
|  | 	int n = 0, i = 0; | ||||||
|  | 	struct node *subnode, **tbl; | ||||||
|  |  | ||||||
|  | 	for_each_child_withdel(node, subnode) | ||||||
|  | 		n++; | ||||||
|  |  | ||||||
|  | 	if (n == 0) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	tbl = xmalloc(n * sizeof(*tbl)); | ||||||
|  |  | ||||||
|  | 	for_each_child_withdel(node, subnode) | ||||||
|  | 		tbl[i++] = subnode; | ||||||
|  |  | ||||||
|  | 	qsort(tbl, n, sizeof(*tbl), cmp_subnode); | ||||||
|  |  | ||||||
|  | 	node->children = tbl[0]; | ||||||
|  | 	for (i = 0; i < (n-1); i++) | ||||||
|  | 		tbl[i]->next_sibling = tbl[i+1]; | ||||||
|  | 	tbl[n-1]->next_sibling = NULL; | ||||||
|  |  | ||||||
|  | 	free(tbl); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void sort_node(struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *c; | ||||||
|  |  | ||||||
|  | 	sort_properties(node); | ||||||
|  | 	sort_subnodes(node); | ||||||
|  | 	for_each_child_withdel(node, c) | ||||||
|  | 		sort_node(c); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void sort_tree(struct dt_info *dti) | ||||||
|  | { | ||||||
|  | 	sort_reserve_entries(dti); | ||||||
|  | 	sort_node(dti->dt); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* utility helper to avoid code duplication */ | ||||||
|  | static struct node *build_and_name_child_node(struct node *parent, char *name) | ||||||
|  | { | ||||||
|  | 	struct node *node; | ||||||
|  |  | ||||||
|  | 	node = build_node(NULL, NULL); | ||||||
|  | 	name_node(node, xstrdup(name)); | ||||||
|  | 	add_child(parent, node); | ||||||
|  |  | ||||||
|  | 	return node; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct node *build_root_node(struct node *dt, char *name) | ||||||
|  | { | ||||||
|  | 	struct node *an; | ||||||
|  |  | ||||||
|  | 	an = get_subnode(dt, name); | ||||||
|  | 	if (!an) | ||||||
|  | 		an = build_and_name_child_node(dt, name); | ||||||
|  |  | ||||||
|  | 	if (!an) | ||||||
|  | 		die("Could not build root node /%s\n", name); | ||||||
|  |  | ||||||
|  | 	return an; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool any_label_tree(struct dt_info *dti, struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *c; | ||||||
|  |  | ||||||
|  | 	if (node->labels) | ||||||
|  | 		return true; | ||||||
|  |  | ||||||
|  | 	for_each_child(node, c) | ||||||
|  | 		if (any_label_tree(dti, c)) | ||||||
|  | 			return true; | ||||||
|  |  | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void generate_label_tree_internal(struct dt_info *dti, | ||||||
|  | 					 struct node *an, struct node *node, | ||||||
|  | 					 bool allocph) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	struct node *c; | ||||||
|  | 	struct property *p; | ||||||
|  | 	struct label *l; | ||||||
|  |  | ||||||
|  | 	/* if there are labels */ | ||||||
|  | 	if (node->labels) { | ||||||
|  |  | ||||||
|  | 		/* now add the label in the node */ | ||||||
|  | 		for_each_label(node->labels, l) { | ||||||
|  |  | ||||||
|  | 			/* check whether the label already exists */ | ||||||
|  | 			p = get_property(an, l->label); | ||||||
|  | 			if (p) { | ||||||
|  | 				fprintf(stderr, "WARNING: label %s already" | ||||||
|  | 					" exists in /%s", l->label, | ||||||
|  | 					an->name); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			/* insert it */ | ||||||
|  | 			p = build_property(l->label, | ||||||
|  | 				data_copy_mem(node->fullpath, | ||||||
|  | 						strlen(node->fullpath) + 1)); | ||||||
|  | 			add_property(an, p); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* force allocation of a phandle for this node */ | ||||||
|  | 		if (allocph) | ||||||
|  | 			(void)get_node_phandle(dt, node); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(node, c) | ||||||
|  | 		generate_label_tree_internal(dti, an, c, allocph); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool any_fixup_tree(struct dt_info *dti, struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *c; | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct marker *m; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		m = prop->val.markers; | ||||||
|  | 		for_each_marker_of_type(m, REF_PHANDLE) { | ||||||
|  | 			if (!get_node_by_ref(dti->dt, m->ref)) | ||||||
|  | 				return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(node, c) { | ||||||
|  | 		if (any_fixup_tree(dti, c)) | ||||||
|  | 			return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void add_fixup_entry(struct dt_info *dti, struct node *fn, | ||||||
|  | 			    struct node *node, struct property *prop, | ||||||
|  | 			    struct marker *m) | ||||||
|  | { | ||||||
|  | 	char *entry; | ||||||
|  |  | ||||||
|  | 	/* m->ref can only be a REF_PHANDLE, but check anyway */ | ||||||
|  | 	assert(m->type == REF_PHANDLE); | ||||||
|  |  | ||||||
|  | 	/* there shouldn't be any ':' in the arguments */ | ||||||
|  | 	if (strchr(node->fullpath, ':') || strchr(prop->name, ':')) | ||||||
|  | 		die("arguments should not contain ':'\n"); | ||||||
|  |  | ||||||
|  | 	xasprintf(&entry, "%s:%s:%u", | ||||||
|  | 			node->fullpath, prop->name, m->offset); | ||||||
|  | 	append_to_property(fn, m->ref, entry, strlen(entry) + 1); | ||||||
|  |  | ||||||
|  | 	free(entry); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void generate_fixups_tree_internal(struct dt_info *dti, | ||||||
|  | 					  struct node *fn, | ||||||
|  | 					  struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	struct node *c; | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct marker *m; | ||||||
|  | 	struct node *refnode; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		m = prop->val.markers; | ||||||
|  | 		for_each_marker_of_type(m, REF_PHANDLE) { | ||||||
|  | 			refnode = get_node_by_ref(dt, m->ref); | ||||||
|  | 			if (!refnode) | ||||||
|  | 				add_fixup_entry(dti, fn, node, prop, m); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(node, c) | ||||||
|  | 		generate_fixups_tree_internal(dti, fn, c); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool any_local_fixup_tree(struct dt_info *dti, struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *c; | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct marker *m; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		m = prop->val.markers; | ||||||
|  | 		for_each_marker_of_type(m, REF_PHANDLE) { | ||||||
|  | 			if (get_node_by_ref(dti->dt, m->ref)) | ||||||
|  | 				return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(node, c) { | ||||||
|  | 		if (any_local_fixup_tree(dti, c)) | ||||||
|  | 			return true; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void add_local_fixup_entry(struct dt_info *dti, | ||||||
|  | 		struct node *lfn, struct node *node, | ||||||
|  | 		struct property *prop, struct marker *m, | ||||||
|  | 		struct node *refnode) | ||||||
|  | { | ||||||
|  | 	struct node *wn, *nwn;	/* local fixup node, walk node, new */ | ||||||
|  | 	uint32_t value_32; | ||||||
|  | 	char **compp; | ||||||
|  | 	int i, depth; | ||||||
|  |  | ||||||
|  | 	/* walk back retreiving depth */ | ||||||
|  | 	depth = 0; | ||||||
|  | 	for (wn = node; wn; wn = wn->parent) | ||||||
|  | 		depth++; | ||||||
|  |  | ||||||
|  | 	/* allocate name array */ | ||||||
|  | 	compp = xmalloc(sizeof(*compp) * depth); | ||||||
|  |  | ||||||
|  | 	/* store names in the array */ | ||||||
|  | 	for (wn = node, i = depth - 1; wn; wn = wn->parent, i--) | ||||||
|  | 		compp[i] = wn->name; | ||||||
|  |  | ||||||
|  | 	/* walk the path components creating nodes if they don't exist */ | ||||||
|  | 	for (wn = lfn, i = 1; i < depth; i++, wn = nwn) { | ||||||
|  | 		/* if no node exists, create it */ | ||||||
|  | 		nwn = get_subnode(wn, compp[i]); | ||||||
|  | 		if (!nwn) | ||||||
|  | 			nwn = build_and_name_child_node(wn, compp[i]); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	free(compp); | ||||||
|  |  | ||||||
|  | 	value_32 = cpu_to_fdt32(m->offset); | ||||||
|  | 	append_to_property(wn, prop->name, &value_32, sizeof(value_32)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void generate_local_fixups_tree_internal(struct dt_info *dti, | ||||||
|  | 						struct node *lfn, | ||||||
|  | 						struct node *node) | ||||||
|  | { | ||||||
|  | 	struct node *dt = dti->dt; | ||||||
|  | 	struct node *c; | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct marker *m; | ||||||
|  | 	struct node *refnode; | ||||||
|  |  | ||||||
|  | 	for_each_property(node, prop) { | ||||||
|  | 		m = prop->val.markers; | ||||||
|  | 		for_each_marker_of_type(m, REF_PHANDLE) { | ||||||
|  | 			refnode = get_node_by_ref(dt, m->ref); | ||||||
|  | 			if (refnode) | ||||||
|  | 				add_local_fixup_entry(dti, lfn, node, prop, m, refnode); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_child(node, c) | ||||||
|  | 		generate_local_fixups_tree_internal(dti, lfn, c); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void generate_label_tree(struct dt_info *dti, char *name, bool allocph) | ||||||
|  | { | ||||||
|  | 	if (!any_label_tree(dti, dti->dt)) | ||||||
|  | 		return; | ||||||
|  | 	generate_label_tree_internal(dti, build_root_node(dti->dt, name), | ||||||
|  | 				     dti->dt, allocph); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void generate_fixups_tree(struct dt_info *dti, char *name) | ||||||
|  | { | ||||||
|  | 	if (!any_fixup_tree(dti, dti->dt)) | ||||||
|  | 		return; | ||||||
|  | 	generate_fixups_tree_internal(dti, build_root_node(dti->dt, name), | ||||||
|  | 				      dti->dt); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void generate_local_fixups_tree(struct dt_info *dti, char *name) | ||||||
|  | { | ||||||
|  | 	if (!any_local_fixup_tree(dti, dti->dt)) | ||||||
|  | 		return; | ||||||
|  | 	generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name), | ||||||
|  | 					    dti->dt); | ||||||
|  | } | ||||||
							
								
								
									
										302
									
								
								scripts/dtc/srcpos.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								scripts/dtc/srcpos.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,302 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc. | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #define _GNU_SOURCE | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  | #include "srcpos.h" | ||||||
|  |  | ||||||
|  | /* A node in our list of directories to search for source/include files */ | ||||||
|  | struct search_path { | ||||||
|  | 	struct search_path *next;	/* next node in list, NULL for end */ | ||||||
|  | 	const char *dirname;		/* name of directory to search */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* This is the list of directories that we search for source files */ | ||||||
|  | static struct search_path *search_path_head, **search_path_tail; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static char *get_dirname(const char *path) | ||||||
|  | { | ||||||
|  | 	const char *slash = strrchr(path, '/'); | ||||||
|  |  | ||||||
|  | 	if (slash) { | ||||||
|  | 		int len = slash - path; | ||||||
|  | 		char *dir = xmalloc(len + 1); | ||||||
|  |  | ||||||
|  | 		memcpy(dir, path, len); | ||||||
|  | 		dir[len] = '\0'; | ||||||
|  | 		return dir; | ||||||
|  | 	} | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | FILE *depfile; /* = NULL */ | ||||||
|  | struct srcfile_state *current_srcfile; /* = NULL */ | ||||||
|  |  | ||||||
|  | /* Detect infinite include recursion. */ | ||||||
|  | #define MAX_SRCFILE_DEPTH     (100) | ||||||
|  | static int srcfile_depth; /* = 0 */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Try to open a file in a given directory. | ||||||
|  |  * | ||||||
|  |  * If the filename is an absolute path, then dirname is ignored. If it is a | ||||||
|  |  * relative path, then we look in that directory for the file. | ||||||
|  |  * | ||||||
|  |  * @param dirname	Directory to look in, or NULL for none | ||||||
|  |  * @param fname		Filename to look for | ||||||
|  |  * @param fp		Set to NULL if file did not open | ||||||
|  |  * @return allocated filename on success (caller must free), NULL on failure | ||||||
|  |  */ | ||||||
|  | static char *try_open(const char *dirname, const char *fname, FILE **fp) | ||||||
|  | { | ||||||
|  | 	char *fullname; | ||||||
|  |  | ||||||
|  | 	if (!dirname || fname[0] == '/') | ||||||
|  | 		fullname = xstrdup(fname); | ||||||
|  | 	else | ||||||
|  | 		fullname = join_path(dirname, fname); | ||||||
|  |  | ||||||
|  | 	*fp = fopen(fullname, "rb"); | ||||||
|  | 	if (!*fp) { | ||||||
|  | 		free(fullname); | ||||||
|  | 		fullname = NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return fullname; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Open a file for read access | ||||||
|  |  * | ||||||
|  |  * If it is a relative filename, we search the full search path for it. | ||||||
|  |  * | ||||||
|  |  * @param fname	Filename to open | ||||||
|  |  * @param fp	Returns pointer to opened FILE, or NULL on failure | ||||||
|  |  * @return pointer to allocated filename, which caller must free | ||||||
|  |  */ | ||||||
|  | static char *fopen_any_on_path(const char *fname, FILE **fp) | ||||||
|  | { | ||||||
|  | 	const char *cur_dir = NULL; | ||||||
|  | 	struct search_path *node; | ||||||
|  | 	char *fullname; | ||||||
|  |  | ||||||
|  | 	/* Try current directory first */ | ||||||
|  | 	assert(fp); | ||||||
|  | 	if (current_srcfile) | ||||||
|  | 		cur_dir = current_srcfile->dir; | ||||||
|  | 	fullname = try_open(cur_dir, fname, fp); | ||||||
|  |  | ||||||
|  | 	/* Failing that, try each search path in turn */ | ||||||
|  | 	for (node = search_path_head; !*fp && node; node = node->next) | ||||||
|  | 		fullname = try_open(node->dirname, fname, fp); | ||||||
|  |  | ||||||
|  | 	return fullname; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | FILE *srcfile_relative_open(const char *fname, char **fullnamep) | ||||||
|  | { | ||||||
|  | 	FILE *f; | ||||||
|  | 	char *fullname; | ||||||
|  |  | ||||||
|  | 	if (streq(fname, "-")) { | ||||||
|  | 		f = stdin; | ||||||
|  | 		fullname = xstrdup("<stdin>"); | ||||||
|  | 	} else { | ||||||
|  | 		fullname = fopen_any_on_path(fname, &f); | ||||||
|  | 		if (!f) | ||||||
|  | 			die("Couldn't open \"%s\": %s\n", fname, | ||||||
|  | 			    strerror(errno)); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (depfile) | ||||||
|  | 		fprintf(depfile, " %s", fullname); | ||||||
|  |  | ||||||
|  | 	if (fullnamep) | ||||||
|  | 		*fullnamep = fullname; | ||||||
|  | 	else | ||||||
|  | 		free(fullname); | ||||||
|  |  | ||||||
|  | 	return f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void srcfile_push(const char *fname) | ||||||
|  | { | ||||||
|  | 	struct srcfile_state *srcfile; | ||||||
|  |  | ||||||
|  | 	if (srcfile_depth++ >= MAX_SRCFILE_DEPTH) | ||||||
|  | 		die("Includes nested too deeply"); | ||||||
|  |  | ||||||
|  | 	srcfile = xmalloc(sizeof(*srcfile)); | ||||||
|  |  | ||||||
|  | 	srcfile->f = srcfile_relative_open(fname, &srcfile->name); | ||||||
|  | 	srcfile->dir = get_dirname(srcfile->name); | ||||||
|  | 	srcfile->prev = current_srcfile; | ||||||
|  |  | ||||||
|  | 	srcfile->lineno = 1; | ||||||
|  | 	srcfile->colno = 1; | ||||||
|  |  | ||||||
|  | 	current_srcfile = srcfile; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool srcfile_pop(void) | ||||||
|  | { | ||||||
|  | 	struct srcfile_state *srcfile = current_srcfile; | ||||||
|  |  | ||||||
|  | 	assert(srcfile); | ||||||
|  |  | ||||||
|  | 	current_srcfile = srcfile->prev; | ||||||
|  |  | ||||||
|  | 	if (fclose(srcfile->f)) | ||||||
|  | 		die("Error closing \"%s\": %s\n", srcfile->name, | ||||||
|  | 		    strerror(errno)); | ||||||
|  |  | ||||||
|  | 	/* FIXME: We allow the srcfile_state structure to leak, | ||||||
|  | 	 * because it could still be referenced from a location | ||||||
|  | 	 * variable being carried through the parser somewhere.  To | ||||||
|  | 	 * fix this we could either allocate all the files from a | ||||||
|  | 	 * table, or use a pool allocator. */ | ||||||
|  |  | ||||||
|  | 	return current_srcfile ? true : false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void srcfile_add_search_path(const char *dirname) | ||||||
|  | { | ||||||
|  | 	struct search_path *node; | ||||||
|  |  | ||||||
|  | 	/* Create the node */ | ||||||
|  | 	node = xmalloc(sizeof(*node)); | ||||||
|  | 	node->next = NULL; | ||||||
|  | 	node->dirname = xstrdup(dirname); | ||||||
|  |  | ||||||
|  | 	/* Add to the end of our list */ | ||||||
|  | 	if (search_path_tail) | ||||||
|  | 		*search_path_tail = node; | ||||||
|  | 	else | ||||||
|  | 		search_path_head = node; | ||||||
|  | 	search_path_tail = &node->next; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * The empty source position. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | struct srcpos srcpos_empty = { | ||||||
|  | 	.first_line = 0, | ||||||
|  | 	.first_column = 0, | ||||||
|  | 	.last_line = 0, | ||||||
|  | 	.last_column = 0, | ||||||
|  | 	.file = NULL, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define TAB_SIZE      8 | ||||||
|  |  | ||||||
|  | void srcpos_update(struct srcpos *pos, const char *text, int len) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	pos->file = current_srcfile; | ||||||
|  |  | ||||||
|  | 	pos->first_line = current_srcfile->lineno; | ||||||
|  | 	pos->first_column = current_srcfile->colno; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < len; i++) | ||||||
|  | 		if (text[i] == '\n') { | ||||||
|  | 			current_srcfile->lineno++; | ||||||
|  | 			current_srcfile->colno = 1; | ||||||
|  | 		} else if (text[i] == '\t') { | ||||||
|  | 			current_srcfile->colno = | ||||||
|  | 				ALIGN(current_srcfile->colno, TAB_SIZE); | ||||||
|  | 		} else { | ||||||
|  | 			current_srcfile->colno++; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	pos->last_line = current_srcfile->lineno; | ||||||
|  | 	pos->last_column = current_srcfile->colno; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct srcpos * | ||||||
|  | srcpos_copy(struct srcpos *pos) | ||||||
|  | { | ||||||
|  | 	struct srcpos *pos_new; | ||||||
|  |  | ||||||
|  | 	pos_new = xmalloc(sizeof(struct srcpos)); | ||||||
|  | 	memcpy(pos_new, pos, sizeof(struct srcpos)); | ||||||
|  |  | ||||||
|  | 	return pos_new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | char * | ||||||
|  | srcpos_string(struct srcpos *pos) | ||||||
|  | { | ||||||
|  | 	const char *fname = "<no-file>"; | ||||||
|  | 	char *pos_str; | ||||||
|  |  | ||||||
|  | 	if (pos->file && pos->file->name) | ||||||
|  | 		fname = pos->file->name; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	if (pos->first_line != pos->last_line) | ||||||
|  | 		xasprintf(&pos_str, "%s:%d.%d-%d.%d", fname, | ||||||
|  | 			  pos->first_line, pos->first_column, | ||||||
|  | 			  pos->last_line, pos->last_column); | ||||||
|  | 	else if (pos->first_column != pos->last_column) | ||||||
|  | 		xasprintf(&pos_str, "%s:%d.%d-%d", fname, | ||||||
|  | 			  pos->first_line, pos->first_column, | ||||||
|  | 			  pos->last_column); | ||||||
|  | 	else | ||||||
|  | 		xasprintf(&pos_str, "%s:%d.%d", fname, | ||||||
|  | 			  pos->first_line, pos->first_column); | ||||||
|  |  | ||||||
|  | 	return pos_str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void srcpos_verror(struct srcpos *pos, const char *prefix, | ||||||
|  | 		   const char *fmt, va_list va) | ||||||
|  | { | ||||||
|  | 	char *srcstr; | ||||||
|  |  | ||||||
|  | 	srcstr = srcpos_string(pos); | ||||||
|  |  | ||||||
|  | 	fprintf(stderr, "%s: %s ", prefix, srcstr); | ||||||
|  | 	vfprintf(stderr, fmt, va); | ||||||
|  | 	fprintf(stderr, "\n"); | ||||||
|  |  | ||||||
|  | 	free(srcstr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void srcpos_error(struct srcpos *pos, const char *prefix, | ||||||
|  | 		  const char *fmt, ...) | ||||||
|  | { | ||||||
|  | 	va_list va; | ||||||
|  |  | ||||||
|  | 	va_start(va, fmt); | ||||||
|  | 	srcpos_verror(pos, prefix, fmt, va); | ||||||
|  | 	va_end(va); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void srcpos_set_line(char *f, int l) | ||||||
|  | { | ||||||
|  | 	current_srcfile->name = f; | ||||||
|  | 	current_srcfile->lineno = l; | ||||||
|  | } | ||||||
							
								
								
									
										118
									
								
								scripts/dtc/srcpos.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								scripts/dtc/srcpos.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2007 Jon Loeliger, Freescale Semiconductor, Inc. | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _SRCPOS_H_ | ||||||
|  | #define _SRCPOS_H_ | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  |  | ||||||
|  | struct srcfile_state { | ||||||
|  | 	FILE *f; | ||||||
|  | 	char *name; | ||||||
|  | 	char *dir; | ||||||
|  | 	int lineno, colno; | ||||||
|  | 	struct srcfile_state *prev; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | extern FILE *depfile; /* = NULL */ | ||||||
|  | extern struct srcfile_state *current_srcfile; /* = NULL */ | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Open a source file. | ||||||
|  |  * | ||||||
|  |  * If the source file is a relative pathname, then it is searched for in the | ||||||
|  |  * current directory (the directory of the last source file read) and after | ||||||
|  |  * that in the search path. | ||||||
|  |  * | ||||||
|  |  * We work through the search path in order from the first path specified to | ||||||
|  |  * the last. | ||||||
|  |  * | ||||||
|  |  * If the file is not found, then this function does not return, but calls | ||||||
|  |  * die(). | ||||||
|  |  * | ||||||
|  |  * @param fname		Filename to search | ||||||
|  |  * @param fullnamep	If non-NULL, it is set to the allocated filename of the | ||||||
|  |  *			file that was opened. The caller is then responsible | ||||||
|  |  *			for freeing the pointer. | ||||||
|  |  * @return pointer to opened FILE | ||||||
|  |  */ | ||||||
|  | FILE *srcfile_relative_open(const char *fname, char **fullnamep); | ||||||
|  |  | ||||||
|  | void srcfile_push(const char *fname); | ||||||
|  | bool srcfile_pop(void); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Add a new directory to the search path for input files | ||||||
|  |  * | ||||||
|  |  * The new path is added at the end of the list. | ||||||
|  |  * | ||||||
|  |  * @param dirname	Directory to add | ||||||
|  |  */ | ||||||
|  | void srcfile_add_search_path(const char *dirname); | ||||||
|  |  | ||||||
|  | struct srcpos { | ||||||
|  |     int first_line; | ||||||
|  |     int first_column; | ||||||
|  |     int last_line; | ||||||
|  |     int last_column; | ||||||
|  |     struct srcfile_state *file; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define YYLTYPE struct srcpos | ||||||
|  |  | ||||||
|  | #define YYLLOC_DEFAULT(Current, Rhs, N)						\ | ||||||
|  | 	do {									\ | ||||||
|  | 		if (N) {							\ | ||||||
|  | 			(Current).first_line = YYRHSLOC(Rhs, 1).first_line;	\ | ||||||
|  | 			(Current).first_column = YYRHSLOC(Rhs, 1).first_column;	\ | ||||||
|  | 			(Current).last_line = YYRHSLOC(Rhs, N).last_line;	\ | ||||||
|  | 			(Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\ | ||||||
|  | 			(Current).file = YYRHSLOC(Rhs, N).file;			\ | ||||||
|  | 		} else {							\ | ||||||
|  | 			(Current).first_line = (Current).last_line =		\ | ||||||
|  | 				YYRHSLOC(Rhs, 0).last_line;			\ | ||||||
|  | 			(Current).first_column = (Current).last_column =	\ | ||||||
|  | 				YYRHSLOC(Rhs, 0).last_column;			\ | ||||||
|  | 			(Current).file = YYRHSLOC (Rhs, 0).file;		\ | ||||||
|  | 		}								\ | ||||||
|  | 	} while (0) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Fictional source position used for IR nodes that are | ||||||
|  |  * created without otherwise knowing a true source position. | ||||||
|  |  * For example,constant definitions from the command line. | ||||||
|  |  */ | ||||||
|  | extern struct srcpos srcpos_empty; | ||||||
|  |  | ||||||
|  | extern void srcpos_update(struct srcpos *pos, const char *text, int len); | ||||||
|  | extern struct srcpos *srcpos_copy(struct srcpos *pos); | ||||||
|  | extern char *srcpos_string(struct srcpos *pos); | ||||||
|  |  | ||||||
|  | extern void srcpos_verror(struct srcpos *pos, const char *prefix, | ||||||
|  | 			  const char *fmt, va_list va) | ||||||
|  | 	__attribute__((format(printf, 3, 0))); | ||||||
|  | extern void srcpos_error(struct srcpos *pos, const char *prefix, | ||||||
|  | 			 const char *fmt, ...) | ||||||
|  | 	__attribute__((format(printf, 3, 4))); | ||||||
|  |  | ||||||
|  | extern void srcpos_set_line(char *f, int l); | ||||||
|  |  | ||||||
|  | #endif /* _SRCPOS_H_ */ | ||||||
							
								
								
									
										284
									
								
								scripts/dtc/treesource.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								scripts/dtc/treesource.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,284 @@ | |||||||
|  | /* | ||||||
|  |  * (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation.  2005. | ||||||
|  |  * | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "dtc.h" | ||||||
|  | #include "srcpos.h" | ||||||
|  |  | ||||||
|  | extern FILE *yyin; | ||||||
|  | extern int yyparse(void); | ||||||
|  | extern YYLTYPE yylloc; | ||||||
|  |  | ||||||
|  | struct dt_info *parser_output; | ||||||
|  | bool treesource_error; | ||||||
|  |  | ||||||
|  | struct dt_info *dt_from_source(const char *fname) | ||||||
|  | { | ||||||
|  | 	parser_output = NULL; | ||||||
|  | 	treesource_error = false; | ||||||
|  |  | ||||||
|  | 	srcfile_push(fname); | ||||||
|  | 	yyin = current_srcfile->f; | ||||||
|  | 	yylloc.file = current_srcfile; | ||||||
|  |  | ||||||
|  | 	if (yyparse() != 0) | ||||||
|  | 		die("Unable to parse input tree\n"); | ||||||
|  |  | ||||||
|  | 	if (treesource_error) | ||||||
|  | 		die("Syntax error parsing input tree\n"); | ||||||
|  |  | ||||||
|  | 	return parser_output; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void write_prefix(FILE *f, int level) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < level; i++) | ||||||
|  | 		fputc('\t', f); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool isstring(char c) | ||||||
|  | { | ||||||
|  | 	return (isprint((unsigned char)c) | ||||||
|  | 		|| (c == '\0') | ||||||
|  | 		|| strchr("\a\b\t\n\v\f\r", c)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void write_propval_string(FILE *f, struct data val) | ||||||
|  | { | ||||||
|  | 	const char *str = val.val; | ||||||
|  | 	int i; | ||||||
|  | 	struct marker *m = val.markers; | ||||||
|  |  | ||||||
|  | 	assert(str[val.len-1] == '\0'); | ||||||
|  |  | ||||||
|  | 	while (m && (m->offset == 0)) { | ||||||
|  | 		if (m->type == LABEL) | ||||||
|  | 			fprintf(f, "%s: ", m->ref); | ||||||
|  | 		m = m->next; | ||||||
|  | 	} | ||||||
|  | 	fprintf(f, "\""); | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < (val.len-1); i++) { | ||||||
|  | 		char c = str[i]; | ||||||
|  |  | ||||||
|  | 		switch (c) { | ||||||
|  | 		case '\a': | ||||||
|  | 			fprintf(f, "\\a"); | ||||||
|  | 			break; | ||||||
|  | 		case '\b': | ||||||
|  | 			fprintf(f, "\\b"); | ||||||
|  | 			break; | ||||||
|  | 		case '\t': | ||||||
|  | 			fprintf(f, "\\t"); | ||||||
|  | 			break; | ||||||
|  | 		case '\n': | ||||||
|  | 			fprintf(f, "\\n"); | ||||||
|  | 			break; | ||||||
|  | 		case '\v': | ||||||
|  | 			fprintf(f, "\\v"); | ||||||
|  | 			break; | ||||||
|  | 		case '\f': | ||||||
|  | 			fprintf(f, "\\f"); | ||||||
|  | 			break; | ||||||
|  | 		case '\r': | ||||||
|  | 			fprintf(f, "\\r"); | ||||||
|  | 			break; | ||||||
|  | 		case '\\': | ||||||
|  | 			fprintf(f, "\\\\"); | ||||||
|  | 			break; | ||||||
|  | 		case '\"': | ||||||
|  | 			fprintf(f, "\\\""); | ||||||
|  | 			break; | ||||||
|  | 		case '\0': | ||||||
|  | 			fprintf(f, "\", "); | ||||||
|  | 			while (m && (m->offset <= (i + 1))) { | ||||||
|  | 				if (m->type == LABEL) { | ||||||
|  | 					assert(m->offset == (i+1)); | ||||||
|  | 					fprintf(f, "%s: ", m->ref); | ||||||
|  | 				} | ||||||
|  | 				m = m->next; | ||||||
|  | 			} | ||||||
|  | 			fprintf(f, "\""); | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			if (isprint((unsigned char)c)) | ||||||
|  | 				fprintf(f, "%c", c); | ||||||
|  | 			else | ||||||
|  | 				fprintf(f, "\\x%02hhx", c); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	fprintf(f, "\""); | ||||||
|  |  | ||||||
|  | 	/* Wrap up any labels at the end of the value */ | ||||||
|  | 	for_each_marker_of_type(m, LABEL) { | ||||||
|  | 		assert (m->offset == val.len); | ||||||
|  | 		fprintf(f, " %s:", m->ref); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void write_propval_cells(FILE *f, struct data val) | ||||||
|  | { | ||||||
|  | 	void *propend = val.val + val.len; | ||||||
|  | 	cell_t *cp = (cell_t *)val.val; | ||||||
|  | 	struct marker *m = val.markers; | ||||||
|  |  | ||||||
|  | 	fprintf(f, "<"); | ||||||
|  | 	for (;;) { | ||||||
|  | 		while (m && (m->offset <= ((char *)cp - val.val))) { | ||||||
|  | 			if (m->type == LABEL) { | ||||||
|  | 				assert(m->offset == ((char *)cp - val.val)); | ||||||
|  | 				fprintf(f, "%s: ", m->ref); | ||||||
|  | 			} | ||||||
|  | 			m = m->next; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fprintf(f, "0x%x", fdt32_to_cpu(*cp++)); | ||||||
|  | 		if ((void *)cp >= propend) | ||||||
|  | 			break; | ||||||
|  | 		fprintf(f, " "); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Wrap up any labels at the end of the value */ | ||||||
|  | 	for_each_marker_of_type(m, LABEL) { | ||||||
|  | 		assert (m->offset == val.len); | ||||||
|  | 		fprintf(f, " %s:", m->ref); | ||||||
|  | 	} | ||||||
|  | 	fprintf(f, ">"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void write_propval_bytes(FILE *f, struct data val) | ||||||
|  | { | ||||||
|  | 	void *propend = val.val + val.len; | ||||||
|  | 	const char *bp = val.val; | ||||||
|  | 	struct marker *m = val.markers; | ||||||
|  |  | ||||||
|  | 	fprintf(f, "["); | ||||||
|  | 	for (;;) { | ||||||
|  | 		while (m && (m->offset == (bp-val.val))) { | ||||||
|  | 			if (m->type == LABEL) | ||||||
|  | 				fprintf(f, "%s: ", m->ref); | ||||||
|  | 			m = m->next; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		fprintf(f, "%02hhx", (unsigned char)(*bp++)); | ||||||
|  | 		if ((const void *)bp >= propend) | ||||||
|  | 			break; | ||||||
|  | 		fprintf(f, " "); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Wrap up any labels at the end of the value */ | ||||||
|  | 	for_each_marker_of_type(m, LABEL) { | ||||||
|  | 		assert (m->offset == val.len); | ||||||
|  | 		fprintf(f, " %s:", m->ref); | ||||||
|  | 	} | ||||||
|  | 	fprintf(f, "]"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void write_propval(FILE *f, struct property *prop) | ||||||
|  | { | ||||||
|  | 	int len = prop->val.len; | ||||||
|  | 	const char *p = prop->val.val; | ||||||
|  | 	struct marker *m = prop->val.markers; | ||||||
|  | 	int nnotstring = 0, nnul = 0; | ||||||
|  | 	int nnotstringlbl = 0, nnotcelllbl = 0; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	if (len == 0) { | ||||||
|  | 		fprintf(f, ";\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < len; i++) { | ||||||
|  | 		if (! isstring(p[i])) | ||||||
|  | 			nnotstring++; | ||||||
|  | 		if (p[i] == '\0') | ||||||
|  | 			nnul++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for_each_marker_of_type(m, LABEL) { | ||||||
|  | 		if ((m->offset > 0) && (prop->val.val[m->offset - 1] != '\0')) | ||||||
|  | 			nnotstringlbl++; | ||||||
|  | 		if ((m->offset % sizeof(cell_t)) != 0) | ||||||
|  | 			nnotcelllbl++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fprintf(f, " = "); | ||||||
|  | 	if ((p[len-1] == '\0') && (nnotstring == 0) && (nnul < (len-nnul)) | ||||||
|  | 	    && (nnotstringlbl == 0)) { | ||||||
|  | 		write_propval_string(f, prop->val); | ||||||
|  | 	} else if (((len % sizeof(cell_t)) == 0) && (nnotcelllbl == 0)) { | ||||||
|  | 		write_propval_cells(f, prop->val); | ||||||
|  | 	} else { | ||||||
|  | 		write_propval_bytes(f, prop->val); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	fprintf(f, ";\n"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void write_tree_source_node(FILE *f, struct node *tree, int level) | ||||||
|  | { | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct node *child; | ||||||
|  | 	struct label *l; | ||||||
|  |  | ||||||
|  | 	write_prefix(f, level); | ||||||
|  | 	for_each_label(tree->labels, l) | ||||||
|  | 		fprintf(f, "%s: ", l->label); | ||||||
|  | 	if (tree->name && (*tree->name)) | ||||||
|  | 		fprintf(f, "%s {\n", tree->name); | ||||||
|  | 	else | ||||||
|  | 		fprintf(f, "/ {\n"); | ||||||
|  |  | ||||||
|  | 	for_each_property(tree, prop) { | ||||||
|  | 		write_prefix(f, level+1); | ||||||
|  | 		for_each_label(prop->labels, l) | ||||||
|  | 			fprintf(f, "%s: ", l->label); | ||||||
|  | 		fprintf(f, "%s", prop->name); | ||||||
|  | 		write_propval(f, prop); | ||||||
|  | 	} | ||||||
|  | 	for_each_child(tree, child) { | ||||||
|  | 		fprintf(f, "\n"); | ||||||
|  | 		write_tree_source_node(f, child, level+1); | ||||||
|  | 	} | ||||||
|  | 	write_prefix(f, level); | ||||||
|  | 	fprintf(f, "};\n"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void dt_to_source(FILE *f, struct dt_info *dti) | ||||||
|  | { | ||||||
|  | 	struct reserve_info *re; | ||||||
|  |  | ||||||
|  | 	fprintf(f, "/dts-v1/;\n\n"); | ||||||
|  |  | ||||||
|  | 	for (re = dti->reservelist; re; re = re->next) { | ||||||
|  | 		struct label *l; | ||||||
|  |  | ||||||
|  | 		for_each_label(re->labels, l) | ||||||
|  | 			fprintf(f, "%s: ", l->label); | ||||||
|  | 		fprintf(f, "/memreserve/\t0x%016llx 0x%016llx;\n", | ||||||
|  | 			(unsigned long long)re->re.address, | ||||||
|  | 			(unsigned long long)re->re.size); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	write_tree_source_node(f, dti->dt, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										81
									
								
								scripts/dtc/update-dtc-source.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										81
									
								
								scripts/dtc/update-dtc-source.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | #!/bin/sh | ||||||
|  | # Simple script to update the version of DTC carried by the Linux kernel | ||||||
|  | # | ||||||
|  | # This script assumes that the dtc and the linux git trees are in the | ||||||
|  | # same directory. After building dtc in the dtc directory, it copies the | ||||||
|  | # source files and generated source files into the scripts/dtc directory | ||||||
|  | # in the kernel and creates a git commit updating them to the new | ||||||
|  | # version. | ||||||
|  | # | ||||||
|  | # Usage: from the top level Linux source tree, run: | ||||||
|  | # $ ./scripts/dtc/update-dtc-source.sh | ||||||
|  | # | ||||||
|  | # The script will change into the dtc tree, build and test dtc, copy the | ||||||
|  | # relevant files into the kernel tree and create a git commit. The commit | ||||||
|  | # message will need to be modified to reflect the version of DTC being | ||||||
|  | # imported | ||||||
|  | # | ||||||
|  | # TODO: | ||||||
|  | # This script is pretty basic, but it is seldom used so a few manual tasks | ||||||
|  | # aren't a big deal. If anyone is interested in making it more robust, the | ||||||
|  | # the following would be nice: | ||||||
|  | # * Actually fail to complete if any testcase fails. | ||||||
|  | #   - The dtc "make check" target needs to return a failure | ||||||
|  | # * Extract the version number from the dtc repo for the commit message | ||||||
|  | # * Build dtc in the kernel tree | ||||||
|  | # * run 'make check" on dtc built from the kernel tree | ||||||
|  |  | ||||||
|  | set -ev | ||||||
|  |  | ||||||
|  | DTC_UPSTREAM_PATH=`pwd`/../dtc | ||||||
|  | DTC_LINUX_PATH=`pwd`/scripts/dtc | ||||||
|  |  | ||||||
|  | DTC_SOURCE="checks.c data.c dtc.c dtc.h flattree.c fstree.c livetree.c srcpos.c \ | ||||||
|  | 		srcpos.h treesource.c util.c util.h version_gen.h Makefile.dtc \ | ||||||
|  | 		dtc-lexer.l dtc-parser.y" | ||||||
|  | DTC_GENERATED="dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h" | ||||||
|  | LIBFDT_SOURCE="Makefile.libfdt fdt.c fdt.h fdt_empty_tree.c fdt_ro.c fdt_rw.c fdt_strerror.c fdt_sw.c fdt_wip.c libfdt.h libfdt_env.h libfdt_internal.h" | ||||||
|  |  | ||||||
|  | get_last_dtc_version() { | ||||||
|  | 	git log --oneline scripts/dtc/ | grep 'upstream' | head -1 | sed -e 's/^.* \(.*\)/\1/' | ||||||
|  | } | ||||||
|  |  | ||||||
|  | last_dtc_ver=$(get_last_dtc_version) | ||||||
|  |  | ||||||
|  | # Build DTC | ||||||
|  | cd $DTC_UPSTREAM_PATH | ||||||
|  | make clean | ||||||
|  | make check | ||||||
|  | dtc_version=$(git describe HEAD) | ||||||
|  | dtc_log=$(git log --oneline ${last_dtc_ver}..) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Copy the files into the Linux tree | ||||||
|  | cd $DTC_LINUX_PATH | ||||||
|  | for f in $DTC_SOURCE; do | ||||||
|  | 	cp ${DTC_UPSTREAM_PATH}/${f} ${f} | ||||||
|  | 	git add ${f} | ||||||
|  | done | ||||||
|  | for f in $DTC_GENERATED; do | ||||||
|  | 	cp ${DTC_UPSTREAM_PATH}/$f ${f}_shipped | ||||||
|  | 	git add ${f}_shipped | ||||||
|  | done | ||||||
|  | for f in $LIBFDT_SOURCE; do | ||||||
|  |        cp ${DTC_UPSTREAM_PATH}/libfdt/${f} libfdt/${f} | ||||||
|  |        git add libfdt/${f} | ||||||
|  | done | ||||||
|  |  | ||||||
|  | sed -i -- 's/#include <libfdt_env.h>/#include "libfdt_env.h"/g' ./libfdt/libfdt.h | ||||||
|  | sed -i -- 's/#include <fdt.h>/#include "fdt.h"/g' ./libfdt/libfdt.h | ||||||
|  | git add ./libfdt/libfdt.h | ||||||
|  |  | ||||||
|  | commit_msg=$(cat << EOF | ||||||
|  | scripts/dtc: Update to upstream version ${dtc_version} | ||||||
|  |  | ||||||
|  | This adds the following commits from upstream: | ||||||
|  |  | ||||||
|  | ${dtc_log} | ||||||
|  | EOF | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | git commit -e -v -s -m "${commit_msg}" | ||||||
							
								
								
									
										473
									
								
								scripts/dtc/util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								scripts/dtc/util.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,473 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright 2011 The Chromium Authors, All Rights Reserved. | ||||||
|  |  * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc. | ||||||
|  |  * | ||||||
|  |  * util_is_printable_string contributed by | ||||||
|  |  *	Pantelis Antoniou <pantelis.antoniou AT gmail.com> | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <assert.h> | ||||||
|  |  | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
|  | #include "libfdt.h" | ||||||
|  | #include "util.h" | ||||||
|  | #include "version_gen.h" | ||||||
|  |  | ||||||
|  | char *xstrdup(const char *s) | ||||||
|  | { | ||||||
|  | 	int len = strlen(s) + 1; | ||||||
|  | 	char *d = xmalloc(len); | ||||||
|  |  | ||||||
|  | 	memcpy(d, s, len); | ||||||
|  |  | ||||||
|  | 	return d; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* based in part from (3) vsnprintf */ | ||||||
|  | int xasprintf(char **strp, const char *fmt, ...) | ||||||
|  | { | ||||||
|  | 	int n, size = 128;	/* start with 128 bytes */ | ||||||
|  | 	char *p; | ||||||
|  | 	va_list ap; | ||||||
|  |  | ||||||
|  | 	/* initial pointer is NULL making the fist realloc to be malloc */ | ||||||
|  | 	p = NULL; | ||||||
|  | 	while (1) { | ||||||
|  | 		p = xrealloc(p, size); | ||||||
|  |  | ||||||
|  | 		/* Try to print in the allocated space. */ | ||||||
|  | 		va_start(ap, fmt); | ||||||
|  | 		n = vsnprintf(p, size, fmt, ap); | ||||||
|  | 		va_end(ap); | ||||||
|  |  | ||||||
|  | 		/* If that worked, return the string. */ | ||||||
|  | 		if (n > -1 && n < size) | ||||||
|  | 			break; | ||||||
|  | 		/* Else try again with more space. */ | ||||||
|  | 		if (n > -1)	/* glibc 2.1 */ | ||||||
|  | 			size = n + 1; /* precisely what is needed */ | ||||||
|  | 		else		/* glibc 2.0 */ | ||||||
|  | 			size *= 2; /* twice the old size */ | ||||||
|  | 	} | ||||||
|  | 	*strp = p; | ||||||
|  | 	return strlen(p); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | char *join_path(const char *path, const char *name) | ||||||
|  | { | ||||||
|  | 	int lenp = strlen(path); | ||||||
|  | 	int lenn = strlen(name); | ||||||
|  | 	int len; | ||||||
|  | 	int needslash = 1; | ||||||
|  | 	char *str; | ||||||
|  |  | ||||||
|  | 	len = lenp + lenn + 2; | ||||||
|  | 	if ((lenp > 0) && (path[lenp-1] == '/')) { | ||||||
|  | 		needslash = 0; | ||||||
|  | 		len--; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	str = xmalloc(len); | ||||||
|  | 	memcpy(str, path, lenp); | ||||||
|  | 	if (needslash) { | ||||||
|  | 		str[lenp] = '/'; | ||||||
|  | 		lenp++; | ||||||
|  | 	} | ||||||
|  | 	memcpy(str+lenp, name, lenn+1); | ||||||
|  | 	return str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool util_is_printable_string(const void *data, int len) | ||||||
|  | { | ||||||
|  | 	const char *s = data; | ||||||
|  | 	const char *ss, *se; | ||||||
|  |  | ||||||
|  | 	/* zero length is not */ | ||||||
|  | 	if (len == 0) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	/* must terminate with zero */ | ||||||
|  | 	if (s[len - 1] != '\0') | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	se = s + len; | ||||||
|  |  | ||||||
|  | 	while (s < se) { | ||||||
|  | 		ss = s; | ||||||
|  | 		while (s < se && *s && isprint((unsigned char)*s)) | ||||||
|  | 			s++; | ||||||
|  |  | ||||||
|  | 		/* not zero, or not done yet */ | ||||||
|  | 		if (*s != '\0' || s == ss) | ||||||
|  | 			return 0; | ||||||
|  |  | ||||||
|  | 		s++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Parse a octal encoded character starting at index i in string s.  The | ||||||
|  |  * resulting character will be returned and the index i will be updated to | ||||||
|  |  * point at the character directly after the end of the encoding, this may be | ||||||
|  |  * the '\0' terminator of the string. | ||||||
|  |  */ | ||||||
|  | static char get_oct_char(const char *s, int *i) | ||||||
|  | { | ||||||
|  | 	char x[4]; | ||||||
|  | 	char *endx; | ||||||
|  | 	long val; | ||||||
|  |  | ||||||
|  | 	x[3] = '\0'; | ||||||
|  | 	strncpy(x, s + *i, 3); | ||||||
|  |  | ||||||
|  | 	val = strtol(x, &endx, 8); | ||||||
|  |  | ||||||
|  | 	assert(endx > x); | ||||||
|  |  | ||||||
|  | 	(*i) += endx - x; | ||||||
|  | 	return val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Parse a hexadecimal encoded character starting at index i in string s.  The | ||||||
|  |  * resulting character will be returned and the index i will be updated to | ||||||
|  |  * point at the character directly after the end of the encoding, this may be | ||||||
|  |  * the '\0' terminator of the string. | ||||||
|  |  */ | ||||||
|  | static char get_hex_char(const char *s, int *i) | ||||||
|  | { | ||||||
|  | 	char x[3]; | ||||||
|  | 	char *endx; | ||||||
|  | 	long val; | ||||||
|  |  | ||||||
|  | 	x[2] = '\0'; | ||||||
|  | 	strncpy(x, s + *i, 2); | ||||||
|  |  | ||||||
|  | 	val = strtol(x, &endx, 16); | ||||||
|  | 	if (!(endx  > x)) | ||||||
|  | 		die("\\x used with no following hex digits\n"); | ||||||
|  |  | ||||||
|  | 	(*i) += endx - x; | ||||||
|  | 	return val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | char get_escape_char(const char *s, int *i) | ||||||
|  | { | ||||||
|  | 	char	c = s[*i]; | ||||||
|  | 	int	j = *i + 1; | ||||||
|  | 	char	val; | ||||||
|  |  | ||||||
|  | 	switch (c) { | ||||||
|  | 	case 'a': | ||||||
|  | 		val = '\a'; | ||||||
|  | 		break; | ||||||
|  | 	case 'b': | ||||||
|  | 		val = '\b'; | ||||||
|  | 		break; | ||||||
|  | 	case 't': | ||||||
|  | 		val = '\t'; | ||||||
|  | 		break; | ||||||
|  | 	case 'n': | ||||||
|  | 		val = '\n'; | ||||||
|  | 		break; | ||||||
|  | 	case 'v': | ||||||
|  | 		val = '\v'; | ||||||
|  | 		break; | ||||||
|  | 	case 'f': | ||||||
|  | 		val = '\f'; | ||||||
|  | 		break; | ||||||
|  | 	case 'r': | ||||||
|  | 		val = '\r'; | ||||||
|  | 		break; | ||||||
|  | 	case '0': | ||||||
|  | 	case '1': | ||||||
|  | 	case '2': | ||||||
|  | 	case '3': | ||||||
|  | 	case '4': | ||||||
|  | 	case '5': | ||||||
|  | 	case '6': | ||||||
|  | 	case '7': | ||||||
|  | 		j--; /* need to re-read the first digit as | ||||||
|  | 		      * part of the octal value */ | ||||||
|  | 		val = get_oct_char(s, &j); | ||||||
|  | 		break; | ||||||
|  | 	case 'x': | ||||||
|  | 		val = get_hex_char(s, &j); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		val = c; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	(*i) = j; | ||||||
|  | 	return val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len) | ||||||
|  | { | ||||||
|  | 	int fd = 0;	/* assume stdin */ | ||||||
|  | 	char *buf = NULL; | ||||||
|  | 	off_t bufsize = 1024, offset = 0; | ||||||
|  | 	int ret = 0; | ||||||
|  |  | ||||||
|  | 	*buffp = NULL; | ||||||
|  | 	if (strcmp(filename, "-") != 0) { | ||||||
|  | 		fd = open(filename, O_RDONLY); | ||||||
|  | 		if (fd < 0) | ||||||
|  | 			return errno; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Loop until we have read everything */ | ||||||
|  | 	buf = xmalloc(bufsize); | ||||||
|  | 	do { | ||||||
|  | 		/* Expand the buffer to hold the next chunk */ | ||||||
|  | 		if (offset == bufsize) { | ||||||
|  | 			bufsize *= 2; | ||||||
|  | 			buf = xrealloc(buf, bufsize); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ret = read(fd, &buf[offset], bufsize - offset); | ||||||
|  | 		if (ret < 0) { | ||||||
|  | 			ret = errno; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		offset += ret; | ||||||
|  | 	} while (ret != 0); | ||||||
|  |  | ||||||
|  | 	/* Clean up, including closing stdin; return errno on error */ | ||||||
|  | 	close(fd); | ||||||
|  | 	if (ret) | ||||||
|  | 		free(buf); | ||||||
|  | 	else | ||||||
|  | 		*buffp = buf; | ||||||
|  | 	*len = bufsize; | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int utilfdt_read_err(const char *filename, char **buffp) | ||||||
|  | { | ||||||
|  | 	off_t len; | ||||||
|  | 	return utilfdt_read_err_len(filename, buffp, &len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | char *utilfdt_read_len(const char *filename, off_t *len) | ||||||
|  | { | ||||||
|  | 	char *buff; | ||||||
|  | 	int ret = utilfdt_read_err_len(filename, &buff, len); | ||||||
|  |  | ||||||
|  | 	if (ret) { | ||||||
|  | 		fprintf(stderr, "Couldn't open blob from '%s': %s\n", filename, | ||||||
|  | 			strerror(ret)); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	/* Successful read */ | ||||||
|  | 	return buff; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | char *utilfdt_read(const char *filename) | ||||||
|  | { | ||||||
|  | 	off_t len; | ||||||
|  | 	return utilfdt_read_len(filename, &len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int utilfdt_write_err(const char *filename, const void *blob) | ||||||
|  | { | ||||||
|  | 	int fd = 1;	/* assume stdout */ | ||||||
|  | 	int totalsize; | ||||||
|  | 	int offset; | ||||||
|  | 	int ret = 0; | ||||||
|  | 	const char *ptr = blob; | ||||||
|  |  | ||||||
|  | 	if (strcmp(filename, "-") != 0) { | ||||||
|  | 		fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); | ||||||
|  | 		if (fd < 0) | ||||||
|  | 			return errno; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	totalsize = fdt_totalsize(blob); | ||||||
|  | 	offset = 0; | ||||||
|  |  | ||||||
|  | 	while (offset < totalsize) { | ||||||
|  | 		ret = write(fd, ptr + offset, totalsize - offset); | ||||||
|  | 		if (ret < 0) { | ||||||
|  | 			ret = -errno; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		offset += ret; | ||||||
|  | 	} | ||||||
|  | 	/* Close the file/stdin; return errno on error */ | ||||||
|  | 	if (fd != 1) | ||||||
|  | 		close(fd); | ||||||
|  | 	return ret < 0 ? -ret : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int utilfdt_write(const char *filename, const void *blob) | ||||||
|  | { | ||||||
|  | 	int ret = utilfdt_write_err(filename, blob); | ||||||
|  |  | ||||||
|  | 	if (ret) { | ||||||
|  | 		fprintf(stderr, "Couldn't write blob to '%s': %s\n", filename, | ||||||
|  | 			strerror(ret)); | ||||||
|  | 	} | ||||||
|  | 	return ret ? -1 : 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int utilfdt_decode_type(const char *fmt, int *type, int *size) | ||||||
|  | { | ||||||
|  | 	int qualifier = 0; | ||||||
|  |  | ||||||
|  | 	if (!*fmt) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	/* get the conversion qualifier */ | ||||||
|  | 	*size = -1; | ||||||
|  | 	if (strchr("hlLb", *fmt)) { | ||||||
|  | 		qualifier = *fmt++; | ||||||
|  | 		if (qualifier == *fmt) { | ||||||
|  | 			switch (*fmt++) { | ||||||
|  | /* TODO:		case 'l': qualifier = 'L'; break;*/ | ||||||
|  | 			case 'h': | ||||||
|  | 				qualifier = 'b'; | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* we should now have a type */ | ||||||
|  | 	if ((*fmt == '\0') || !strchr("iuxs", *fmt)) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	/* convert qualifier (bhL) to byte size */ | ||||||
|  | 	if (*fmt != 's') | ||||||
|  | 		*size = qualifier == 'b' ? 1 : | ||||||
|  | 				qualifier == 'h' ? 2 : | ||||||
|  | 				qualifier == 'l' ? 4 : -1; | ||||||
|  | 	*type = *fmt++; | ||||||
|  |  | ||||||
|  | 	/* that should be it! */ | ||||||
|  | 	if (*fmt) | ||||||
|  | 		return -1; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void utilfdt_print_data(const char *data, int len) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	const char *s; | ||||||
|  |  | ||||||
|  | 	/* no data, don't print */ | ||||||
|  | 	if (len == 0) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (util_is_printable_string(data, len)) { | ||||||
|  | 		printf(" = "); | ||||||
|  |  | ||||||
|  | 		s = data; | ||||||
|  | 		do { | ||||||
|  | 			printf("\"%s\"", s); | ||||||
|  | 			s += strlen(s) + 1; | ||||||
|  | 			if (s < data + len) | ||||||
|  | 				printf(", "); | ||||||
|  | 		} while (s < data + len); | ||||||
|  |  | ||||||
|  | 	} else if ((len % 4) == 0) { | ||||||
|  | 		const uint32_t *cell = (const uint32_t *)data; | ||||||
|  |  | ||||||
|  | 		printf(" = <"); | ||||||
|  | 		for (i = 0, len /= 4; i < len; i++) | ||||||
|  | 			printf("0x%08x%s", fdt32_to_cpu(cell[i]), | ||||||
|  | 			       i < (len - 1) ? " " : ""); | ||||||
|  | 		printf(">"); | ||||||
|  | 	} else { | ||||||
|  | 		const unsigned char *p = (const unsigned char *)data; | ||||||
|  | 		printf(" = ["); | ||||||
|  | 		for (i = 0; i < len; i++) | ||||||
|  | 			printf("%02x%s", *p++, i < len - 1 ? " " : ""); | ||||||
|  | 		printf("]"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void util_version(void) | ||||||
|  | { | ||||||
|  | 	printf("Version: %s\n", DTC_VERSION); | ||||||
|  | 	exit(0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void util_usage(const char *errmsg, const char *synopsis, | ||||||
|  | 		const char *short_opts, struct option const long_opts[], | ||||||
|  | 		const char * const opts_help[]) | ||||||
|  | { | ||||||
|  | 	FILE *fp = errmsg ? stderr : stdout; | ||||||
|  | 	const char a_arg[] = "<arg>"; | ||||||
|  | 	size_t a_arg_len = strlen(a_arg) + 1; | ||||||
|  | 	size_t i; | ||||||
|  | 	int optlen; | ||||||
|  |  | ||||||
|  | 	fprintf(fp, | ||||||
|  | 		"Usage: %s\n" | ||||||
|  | 		"\n" | ||||||
|  | 		"Options: -[%s]\n", synopsis, short_opts); | ||||||
|  |  | ||||||
|  | 	/* prescan the --long opt length to auto-align */ | ||||||
|  | 	optlen = 0; | ||||||
|  | 	for (i = 0; long_opts[i].name; ++i) { | ||||||
|  | 		/* +1 is for space between --opt and help text */ | ||||||
|  | 		int l = strlen(long_opts[i].name) + 1; | ||||||
|  | 		if (long_opts[i].has_arg == a_argument) | ||||||
|  | 			l += a_arg_len; | ||||||
|  | 		if (optlen < l) | ||||||
|  | 			optlen = l; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for (i = 0; long_opts[i].name; ++i) { | ||||||
|  | 		/* helps when adding new applets or options */ | ||||||
|  | 		assert(opts_help[i] != NULL); | ||||||
|  |  | ||||||
|  | 		/* first output the short flag if it has one */ | ||||||
|  | 		if (long_opts[i].val > '~') | ||||||
|  | 			fprintf(fp, "      "); | ||||||
|  | 		else | ||||||
|  | 			fprintf(fp, "  -%c, ", long_opts[i].val); | ||||||
|  |  | ||||||
|  | 		/* then the long flag */ | ||||||
|  | 		if (long_opts[i].has_arg == no_argument) | ||||||
|  | 			fprintf(fp, "--%-*s", optlen, long_opts[i].name); | ||||||
|  | 		else | ||||||
|  | 			fprintf(fp, "--%s %s%*s", long_opts[i].name, a_arg, | ||||||
|  | 				(int)(optlen - strlen(long_opts[i].name) - a_arg_len), ""); | ||||||
|  |  | ||||||
|  | 		/* finally the help text */ | ||||||
|  | 		fprintf(fp, "%s\n", opts_help[i]); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (errmsg) { | ||||||
|  | 		fprintf(fp, "\nError: %s\n", errmsg); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} else | ||||||
|  | 		exit(EXIT_SUCCESS); | ||||||
|  | } | ||||||
							
								
								
									
										265
									
								
								scripts/dtc/util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								scripts/dtc/util.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,265 @@ | |||||||
|  | #ifndef _UTIL_H | ||||||
|  | #define _UTIL_H | ||||||
|  |  | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <getopt.h> | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Copyright 2011 The Chromium Authors, All Rights Reserved. | ||||||
|  |  * Copyright 2008 Jon Loeliger, Freescale Semiconductor, Inc. | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU General Public License as | ||||||
|  |  * published by the Free Software Foundation; either version 2 of the | ||||||
|  |  * License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  *  This program is distributed in the hope that it will be useful, | ||||||
|  |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  *  General Public License for more details. | ||||||
|  |  * | ||||||
|  |  *  You should have received a copy of the GNU General Public License | ||||||
|  |  *  along with this program; if not, write to the Free Software | ||||||
|  |  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 | ||||||
|  |  *                                                                   USA | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | ||||||
|  |  | ||||||
|  | #ifdef __GNUC__ | ||||||
|  | static inline void | ||||||
|  | __attribute__((noreturn)) __attribute__((format (printf, 1, 2))) | ||||||
|  | die(const char *str, ...) | ||||||
|  | #else | ||||||
|  | static inline void die(const char *str, ...) | ||||||
|  | #endif | ||||||
|  | { | ||||||
|  | 	va_list ap; | ||||||
|  |  | ||||||
|  | 	va_start(ap, str); | ||||||
|  | 	fprintf(stderr, "FATAL ERROR: "); | ||||||
|  | 	vfprintf(stderr, str, ap); | ||||||
|  | 	va_end(ap); | ||||||
|  | 	exit(1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline void *xmalloc(size_t len) | ||||||
|  | { | ||||||
|  | 	void *new = malloc(len); | ||||||
|  |  | ||||||
|  | 	if (!new) | ||||||
|  | 		die("malloc() failed\n"); | ||||||
|  |  | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static inline void *xrealloc(void *p, size_t len) | ||||||
|  | { | ||||||
|  | 	void *new = realloc(p, len); | ||||||
|  |  | ||||||
|  | 	if (!new) | ||||||
|  | 		die("realloc() failed (len=%zd)\n", len); | ||||||
|  |  | ||||||
|  | 	return new; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | extern char *xstrdup(const char *s); | ||||||
|  |  | ||||||
|  | #ifdef __GNUC__ | ||||||
|  | extern int __attribute__((format (printf, 2, 3))) | ||||||
|  | xasprintf(char **strp, const char *fmt, ...); | ||||||
|  | #else | ||||||
|  | extern int xasprintf(char **strp, const char *fmt, ...); | ||||||
|  | #endif | ||||||
|  | extern char *join_path(const char *path, const char *name); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Check a property of a given length to see if it is all printable and | ||||||
|  |  * has a valid terminator. The property can contain either a single string, | ||||||
|  |  * or multiple strings each of non-zero length. | ||||||
|  |  * | ||||||
|  |  * @param data	The string to check | ||||||
|  |  * @param len	The string length including terminator | ||||||
|  |  * @return 1 if a valid printable string, 0 if not | ||||||
|  |  */ | ||||||
|  | bool util_is_printable_string(const void *data, int len); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Parse an escaped character starting at index i in string s.  The resulting | ||||||
|  |  * character will be returned and the index i will be updated to point at the | ||||||
|  |  * character directly after the end of the encoding, this may be the '\0' | ||||||
|  |  * terminator of the string. | ||||||
|  |  */ | ||||||
|  | char get_escape_char(const char *s, int *i); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Read a device tree file into a buffer. This will report any errors on | ||||||
|  |  * stderr. | ||||||
|  |  * | ||||||
|  |  * @param filename	The filename to read, or - for stdin | ||||||
|  |  * @return Pointer to allocated buffer containing fdt, or NULL on error | ||||||
|  |  */ | ||||||
|  | char *utilfdt_read(const char *filename); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Like utilfdt_read(), but also passes back the size of the file read. | ||||||
|  |  * | ||||||
|  |  * @param len		If non-NULL, the amount of data we managed to read | ||||||
|  |  */ | ||||||
|  | char *utilfdt_read_len(const char *filename, off_t *len); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Read a device tree file into a buffer. Does not report errors, but only | ||||||
|  |  * returns them. The value returned can be passed to strerror() to obtain | ||||||
|  |  * an error message for the user. | ||||||
|  |  * | ||||||
|  |  * @param filename	The filename to read, or - for stdin | ||||||
|  |  * @param buffp		Returns pointer to buffer containing fdt | ||||||
|  |  * @return 0 if ok, else an errno value representing the error | ||||||
|  |  */ | ||||||
|  | int utilfdt_read_err(const char *filename, char **buffp); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Like utilfdt_read_err(), but also passes back the size of the file read. | ||||||
|  |  * | ||||||
|  |  * @param len		If non-NULL, the amount of data we managed to read | ||||||
|  |  */ | ||||||
|  | int utilfdt_read_err_len(const char *filename, char **buffp, off_t *len); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Write a device tree buffer to a file. This will report any errors on | ||||||
|  |  * stderr. | ||||||
|  |  * | ||||||
|  |  * @param filename	The filename to write, or - for stdout | ||||||
|  |  * @param blob		Poiner to buffer containing fdt | ||||||
|  |  * @return 0 if ok, -1 on error | ||||||
|  |  */ | ||||||
|  | int utilfdt_write(const char *filename, const void *blob); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Write a device tree buffer to a file. Does not report errors, but only | ||||||
|  |  * returns them. The value returned can be passed to strerror() to obtain | ||||||
|  |  * an error message for the user. | ||||||
|  |  * | ||||||
|  |  * @param filename	The filename to write, or - for stdout | ||||||
|  |  * @param blob		Poiner to buffer containing fdt | ||||||
|  |  * @return 0 if ok, else an errno value representing the error | ||||||
|  |  */ | ||||||
|  | int utilfdt_write_err(const char *filename, const void *blob); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Decode a data type string. The purpose of this string | ||||||
|  |  * | ||||||
|  |  * The string consists of an optional character followed by the type: | ||||||
|  |  *	Modifier characters: | ||||||
|  |  *		hh or b	1 byte | ||||||
|  |  *		h	2 byte | ||||||
|  |  *		l	4 byte, default | ||||||
|  |  * | ||||||
|  |  *	Type character: | ||||||
|  |  *		s	string | ||||||
|  |  *		i	signed integer | ||||||
|  |  *		u	unsigned integer | ||||||
|  |  *		x	hex | ||||||
|  |  * | ||||||
|  |  * TODO: Implement ll modifier (8 bytes) | ||||||
|  |  * TODO: Implement o type (octal) | ||||||
|  |  * | ||||||
|  |  * @param fmt		Format string to process | ||||||
|  |  * @param type		Returns type found(s/d/u/x), or 0 if none | ||||||
|  |  * @param size		Returns size found(1,2,4,8) or 4 if none | ||||||
|  |  * @return 0 if ok, -1 on error (no type given, or other invalid format) | ||||||
|  |  */ | ||||||
|  | int utilfdt_decode_type(const char *fmt, int *type, int *size); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * This is a usage message fragment for the -t option. It is the format | ||||||
|  |  * supported by utilfdt_decode_type. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #define USAGE_TYPE_MSG \ | ||||||
|  | 	"<type>\ts=string, i=int, u=unsigned, x=hex\n" \ | ||||||
|  | 	"\tOptional modifier prefix:\n" \ | ||||||
|  | 	"\t\thh or b=byte, h=2 byte, l=4 byte (default)"; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Print property data in a readable format to stdout | ||||||
|  |  * | ||||||
|  |  * Properties that look like strings will be printed as strings. Otherwise | ||||||
|  |  * the data will be displayed either as cells (if len is a multiple of 4 | ||||||
|  |  * bytes) or bytes. | ||||||
|  |  * | ||||||
|  |  * If len is 0 then this function does nothing. | ||||||
|  |  * | ||||||
|  |  * @param data	Pointers to property data | ||||||
|  |  * @param len	Length of property data | ||||||
|  |  */ | ||||||
|  | void utilfdt_print_data(const char *data, int len); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Show source version and exit | ||||||
|  |  */ | ||||||
|  | void util_version(void) __attribute__((noreturn)); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Show usage and exit | ||||||
|  |  * | ||||||
|  |  * This helps standardize the output of various utils.  You most likely want | ||||||
|  |  * to use the usage() helper below rather than call this. | ||||||
|  |  * | ||||||
|  |  * @param errmsg	If non-NULL, an error message to display | ||||||
|  |  * @param synopsis	The initial example usage text (and possible examples) | ||||||
|  |  * @param short_opts	The string of short options | ||||||
|  |  * @param long_opts	The structure of long options | ||||||
|  |  * @param opts_help	An array of help strings (should align with long_opts) | ||||||
|  |  */ | ||||||
|  | void util_usage(const char *errmsg, const char *synopsis, | ||||||
|  | 		const char *short_opts, struct option const long_opts[], | ||||||
|  | 		const char * const opts_help[]) __attribute__((noreturn)); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Show usage and exit | ||||||
|  |  * | ||||||
|  |  * If you name all your usage variables with usage_xxx, then you can call this | ||||||
|  |  * help macro rather than expanding all arguments yourself. | ||||||
|  |  * | ||||||
|  |  * @param errmsg	If non-NULL, an error message to display | ||||||
|  |  */ | ||||||
|  | #define usage(errmsg) \ | ||||||
|  | 	util_usage(errmsg, usage_synopsis, usage_short_opts, \ | ||||||
|  | 		   usage_long_opts, usage_opts_help) | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Call getopt_long() with standard options | ||||||
|  |  * | ||||||
|  |  * Since all util code runs getopt in the same way, provide a helper. | ||||||
|  |  */ | ||||||
|  | #define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \ | ||||||
|  | 				       usage_long_opts, NULL) | ||||||
|  |  | ||||||
|  | /* Helper for aligning long_opts array */ | ||||||
|  | #define a_argument required_argument | ||||||
|  |  | ||||||
|  | /* Helper for usage_short_opts string constant */ | ||||||
|  | #define USAGE_COMMON_SHORT_OPTS "hV" | ||||||
|  |  | ||||||
|  | /* Helper for usage_long_opts option array */ | ||||||
|  | #define USAGE_COMMON_LONG_OPTS \ | ||||||
|  | 	{"help",      no_argument, NULL, 'h'}, \ | ||||||
|  | 	{"version",   no_argument, NULL, 'V'}, \ | ||||||
|  | 	{NULL,        no_argument, NULL, 0x0} | ||||||
|  |  | ||||||
|  | /* Helper for usage_opts_help array */ | ||||||
|  | #define USAGE_COMMON_OPTS_HELP \ | ||||||
|  | 	"Print this help and exit", \ | ||||||
|  | 	"Print version and exit", \ | ||||||
|  | 	NULL | ||||||
|  |  | ||||||
|  | /* Helper for getopt case statements */ | ||||||
|  | #define case_USAGE_COMMON_FLAGS \ | ||||||
|  | 	case 'h': usage(NULL); \ | ||||||
|  | 	case 'V': util_version(); \ | ||||||
|  | 	case '?': usage("unknown option"); | ||||||
|  |  | ||||||
|  | #endif /* _UTIL_H */ | ||||||
							
								
								
									
										1
									
								
								scripts/dtc/version_gen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								scripts/dtc/version_gen.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | #define DTC_VERSION "DTC 1.4.3" | ||||||
		Reference in New Issue
	
	Block a user