[U-Boot] [PATCH 1/3] init_func: Add fundamental framework

Graeme Russ graeme.russ at gmail.com
Sun Apr 22 17:23:06 CEST 2012


Signed-off-by: Graeme Russ <graeme.russ at gmail.com>
---
 Makefile             |   34 ++-
 common/Makefile      |    2 +
 config.mk            |    2 +
 doc/README.INIT_FUNC |   65 +++
 include/init_func.h  |   37 ++
 tools/Makefile       |    6 +
 tools/mkinitseq.c    | 1512 ++++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 1657 insertions(+), 1 deletions(-)
 create mode 100644 doc/README.INIT_FUNC
 create mode 100644 include/init_func.h
 create mode 100644 tools/mkinitseq.c

diff --git a/Makefile b/Makefile
index cdd4294..62d9f81 100644
--- a/Makefile
+++ b/Makefile
@@ -467,8 +467,40 @@ GEN_UBOOT = \
 			-Map u-boot.map -o u-boot
 endif
 
+ifeq ($(CONFIG_INIT_FUNC),y)
+INIT_SEQ = $(obj)init_seq.o
+
+GEN_UBOOT_INIT = \
+		UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
+		sed  -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
+		cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $$UNDEF_SYM $(__OBJS) \
+			--start-group $(__LIBS) --end-group \
+			-Map u-boot-init.map -o u-boot-init
+
+$(obj)u-boot-init.lds: $(LDSCRIPT)
+		$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -DMAKE_INIT_LDS -P - <$^ >$@
+
+$(obj)u-boot-init:	depend \
+		$(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(obj)u-boot-init.lds
+		$(GEN_UBOOT_INIT)
+
+$(obj)u-boot-init.bin:	$(obj)u-boot-init
+		$(OBJCOPY) -j .initfuncs -O binary $< $@
+
+$(obj)init_seq.c:	depend \
+		$(obj)u-boot-init.bin
+		$(obj)tools/mkinitseq $(obj)u-boot-init.bin $(obj)common/init_seq.c
+
+$(obj)init_seq.o:	depend \
+		$(obj)init_seq.c
+		$(MAKE) BUILD_INIT_SEQ=y -C common all
+else
+INIT_SEQ =
+endif
+
 $(obj)u-boot:	depend \
-		$(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
+		$(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) \
+		$(INIT_SEQ) $(obj)u-boot.lds
 		$(GEN_UBOOT)
 ifeq ($(CONFIG_KALLSYMS),y)
 		smap=`$(call SYSTEM_MAP,u-boot) | \
diff --git a/common/Makefile b/common/Makefile
index d9f10f3..02a4485 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -195,6 +195,8 @@ COBJS-y += dlmalloc.o
 COBJS-y += memsize.o
 COBJS-y += stdio.o
 
+# initialisation sequence (second build stage)
+COBJS-$(BUILD_INIT_SEQ) += init_seq.o
 
 COBJS	:= $(sort $(COBJS-y))
 XCOBJS	:= $(sort $(XCOBJS-y))
diff --git a/config.mk b/config.mk
index fa33e62..2124b84 100644
--- a/config.mk
+++ b/config.mk
@@ -257,6 +257,8 @@ ifneq ($(CONFIG_SYS_TEXT_BASE),)
 LDFLAGS_u-boot += -Ttext $(CONFIG_SYS_TEXT_BASE)
 endif
 
+LDFLAGS_u-boot-init += -T $(obj)u-boot-init.lds $(LDFLAGS_FINAL)
+
 LDFLAGS_u-boot-spl += -T $(obj)u-boot-spl.lds $(LDFLAGS_FINAL)
 ifneq ($(CONFIG_SPL_TEXT_BASE),)
 LDFLAGS_u-boot-spl += -Ttext $(CONFIG_SPL_TEXT_BASE)
diff --git a/doc/README.INIT_FUNC b/doc/README.INIT_FUNC
new file mode 100644
index 0000000..f9a0b18
--- /dev/null
+++ b/doc/README.INIT_FUNC
@@ -0,0 +1,65 @@
+The INIT_FUNC macro allows initialisation functions (i.e. functions which are
+executed before the main loop) to be easily added to the init sequence
+
+
+Specifying an Initialisation Function and is Dependencies
+---------------------------------------------------------
+The format of the INIT_FUNC macro is:
+
+INIT_FUNC(fn, grp, man_reqs, pre_reqs, pst_reqs)
+
+fn is the name of the init function to call. This function must have the
+following prototype:
+
+int foo(void);
+
+Each init function must return 0 to indicate success - any other return value
+indicates failure and the init sequence will stop
+
+grp is the name of the group that the init function belongs to. grp may be
+the same as fn for any individual init function, but between init functions,
+fn and grp must be unique.
+
+The purpose of groups is to allow functions to be grouped together so other
+functions can specify the group as a whole as a dependency rather than having
+to list every function in the group in the dependency list
+
+man_reqs is a space seperated list of functions or groups that MUST exist and
+MUST run BEFORE fn
+
+pre_reqs is a space seperated list of functions or groups that MAY exist and
+(if they do) MUST run BEFORE fn
+
+pst_reqs is a space seperated list of functions or groups that MAY exist and
+(if they do) MUST run AFTER fn
+
+Skipping or Replacing a Function or Group
+-----------------------------------------
+Occassionally, a board may provide a completely seperate implementation for
+an initialisation function that is provided in the common arch, SoC or
+common code.
+
+SKIP_INIT(fn_or_group)
+
+After the initialisation function dependencies are calculated, all functions
+and groups listed in any SKIP_INITs are removed - This may result in
+dependent functions being removed - It is up to the board code developer
+to ensure suitable replacements are in place
+
+REPLACE_INIT(old_fn_or_group, new_fn_or_group)
+
+Like SKIP_INIT but replaces on function with another (or one group with
+another)
+
+Example: In the SoC code yoy may have
+
+INIT_FUNC(init_cpu_f, RESET, , , );
+
+In the board code, you may want a slightly tweaked version, so you might
+have:
+
+int my_new_init_cpu_f(void)
+{
+	...
+}
+REPLACE_INIT(init_cpu_f, my_new_init_cpu_f);
diff --git a/include/init_func.h b/include/init_func.h
new file mode 100644
index 0000000..e4366b8
--- /dev/null
+++ b/include/init_func.h
@@ -0,0 +1,37 @@
+#ifndef __INIT_FUNC_H__
+#define __INIT_FUNC_H__
+
+/*
+ * The requirements for any new initalization function is simple: it is
+ * a function with no parameters which returns an integer return code,
+ * where 0 means "continue" and != 0 means "fatal error, hang the system"
+ */
+typedef int (init_fnc_t) (void);
+
+extern init_fnc_t *init_sequence_f[];
+extern init_fnc_t *init_sequence_f_r[];
+extern init_fnc_t *init_sequence_r[];
+
+#ifdef CONFIG_INIT_FUNC
+#include <linux/compiler.h>
+
+#define INIT_FUNC(fn, grp, man_reqs, pre_reqs, pst_reqs) \
+	static const char __init_func_ ## fn[] __used \
+	__attribute__((__section__(".initfuncs"))) = \
+	"(f:" #fn ":" #grp ":" #man_reqs " | " #pre_reqs " | " #pst_reqs ")\n";
+
+#define SKIP_INIT(fn_or_group) \
+	static const char __skip_init_ ## fn_or_group[] __used \
+	__attribute__((__section__(".initfuncs"))) = \
+	"(s:" #fn_or_group ")\n";
+
+#define REPLACE_INIT(old_fn_or_group, new_fn_or_group) \
+	static const char __replace_init_ ## old_fn_or_group[] __used \
+	__attribute__((__section__(".initfuncs"))) = \
+	"(r:" #old_fn_or_group ":" #new_fn_or_group ")\n";
+#else
+#define INIT_FUNC(fn, group, man_reqs, pre_reqs, post_reqs)
+#define SKIP_INIT(fn_or_group)
+#define REPLACE_INIT(old_fn_or_group, new_fn_or_group)
+#endif
+#endif /* !__INIT_FUNC_H__ */
diff --git a/tools/Makefile b/tools/Makefile
index 8993fdd..e6ba6ef 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -68,6 +68,7 @@ BIN_FILES-$(CONFIG_CMD_LOADS) += img2srec$(SFX)
 BIN_FILES-$(CONFIG_XWAY_SWAP_BYTES) += xway-swap-bytes$(SFX)
 BIN_FILES-y += mkenvimage$(SFX)
 BIN_FILES-y += mkimage$(SFX)
+BIN_FILES-$(CONFIG_INIT_FUNC) += mkinitseq$(SFX)
 BIN_FILES-$(CONFIG_SMDK5250) += mksmdk5250spl$(SFX)
 BIN_FILES-$(CONFIG_MX28) += mxsboot$(SFX)
 BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX)
@@ -95,6 +96,7 @@ NOPED_OBJ_FILES-y += imximage.o
 NOPED_OBJ_FILES-y += omapimage.o
 NOPED_OBJ_FILES-y += mkenvimage.o
 NOPED_OBJ_FILES-y += mkimage.o
+NOPED_OBJ_FILES-$(CONFIG_INIT_FUNC) += mkinitseq.o
 OBJ_FILES-$(CONFIG_SMDK5250) += mkexynosspl.o
 OBJ_FILES-$(CONFIG_MX28) += mxsboot.o
 OBJ_FILES-$(CONFIG_NETCONSOLE) += ncb.o
@@ -215,6 +217,10 @@ $(obj)mkimage$(SFX):	$(obj)aisimage.o \
 	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
 	$(HOSTSTRIP) $@
 
+$(obj)mkinitseq$(SFX):	$(obj)mkinitseq.o
+	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
+	$(HOSTSTRIP) $@
+
 $(obj)mk$(BOARD)spl$(SFX):	$(obj)mkexynosspl.o
 	$(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^
 	$(HOSTSTRIP) $@
diff --git a/tools/mkinitseq.c b/tools/mkinitseq.c
new file mode 100644
index 0000000..b150de4
--- /dev/null
+++ b/tools/mkinitseq.c
@@ -0,0 +1,1512 @@
+/*
+ * (C) Copyright 2012
+ * Graeme Russ <graeme.russ 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
+ */
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+#include "os_support.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <linux/list.h>
+#include <malloc.h>
+
+#include <version.h>
+
+struct func_def {
+	struct list_head list;
+	char *name;
+};
+
+struct init_def {
+	struct list_head list;
+
+	char *function;
+	char *group;
+
+	int cyclic_checked;
+
+	struct list_head mandatory_deps;
+	struct list_head pre_deps;
+	struct list_head post_deps;
+};
+
+struct skip_def {
+	struct list_head list;
+	char *name;
+};
+
+struct replace_def {
+	struct list_head list;
+	char *old_name;
+	char *new_name;
+};
+
+
+struct init_group {
+	struct list_head list;
+
+	char *name;
+
+	struct list_head functions;
+};
+
+struct list_head init_defs;
+struct list_head skip_defs;
+struct list_head replace_defs;
+
+struct list_head mandatory_functions;
+struct list_head init_groups;
+
+struct list_head init_sequence;
+
+/* These are the initialisation sequence placeholders */
+static const char default_init_reset[] = "f:RESET:RESET: |  | SDRAM";
+static const char default_init_sdram[] = "f:SDRAM:SDRAM: RESET |  | RELOC";
+static const char default_init_reloc[] = "f:RELOC:RELOC: SDRAM |  | ";
+
+static void free_function_list(struct list_head *function_list)
+{
+	struct func_def *tmp;
+
+	while (!list_empty(function_list)) {
+
+		tmp = list_first_entry(function_list, struct func_def, list);
+
+		free(tmp->name);
+		list_del(&tmp->list);
+	}
+}
+
+static int function_exists(const char *function)
+{
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	list_for_each(init_def_pos , &init_defs) {
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (!strcmp(init_def->function, function))
+			return 1;
+	}
+
+	return 0;
+}
+
+static int check_for_empty_groups(void)
+{
+	int err = 0;
+	struct list_head *group_pos;
+	struct init_group *init_group;
+
+	/* Look for an existing group */
+	list_for_each(group_pos, &init_groups)
+	{
+		init_group = list_entry(group_pos, struct init_group, list);
+
+		if (list_empty(&init_group->functions)) {
+			fprintf(stderr,
+				"Empty init group '%s'\n",
+				init_group->name);
+			err = 1;
+		}
+	}
+
+	return err;
+}
+
+static struct init_def *pop_first_independent_function(void)
+{
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (list_empty(&init_def->pre_deps)) {
+			list_del(init_def_pos);
+			return init_def;
+		}
+	}
+
+	return NULL;
+}
+
+static int find_or_create_group(const char *group,
+				struct init_group **init_group)
+{
+	struct list_head *group_pos;
+
+	/* Look for an existing group */
+	list_for_each(group_pos, &init_groups)
+	{
+		*init_group = list_entry(group_pos, struct init_group, list);
+
+		if (!strcmp((*init_group)->name, group))
+			return 0;
+	}
+
+	/* No existing group found - Create a new group */
+	*init_group = malloc(sizeof(struct init_group));
+
+	if (!(*init_group))
+		return -ENOMEM;
+
+	(*init_group)->name = strdup(group);
+
+	if (!(*init_group)->name) {
+		free(*init_group);
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&(*init_group)->functions);
+	list_add(&(*init_group)->list, &init_groups);
+
+	return 0;
+}
+
+static int add_function_to_group(const char *function,
+				 const char *group)
+{
+	int err;
+	struct init_group *init_group;
+	struct func_def *func_def;
+
+	if (!strcmp(group, ""))
+		return 0;
+
+	err = find_or_create_group(group, &init_group);
+	if (err)
+		return err;
+
+	/* Add the function to the group */
+	func_def = malloc(sizeof(struct func_def));
+
+	if (!func_def)
+		return -ENOMEM;
+
+	func_def->name = strdup(function);
+
+	if (!func_def->name) {
+		free(func_def);
+		return -ENOMEM;
+	}
+
+	list_add(&func_def->list, &init_group->functions);
+
+	return 0;
+}
+
+static void delete_group(const char *group)
+{
+	struct list_head *init_group_pos;
+	struct list_head *q;
+	struct init_group *init_group;
+
+
+	list_for_each_safe(init_group_pos, q, &init_groups) {
+		init_group = list_entry(init_group_pos,
+					struct init_group,
+					list);
+
+		if (!strcmp(init_group->name, group)) {
+			free(init_group->name);
+			free_function_list(&init_group->functions);
+
+			list_del(init_group_pos);
+
+			free(init_group);
+		}
+	}
+
+}
+
+static void delete_dep_from_init_defs(const char *dep)
+{
+	struct list_head *init_def_pos;
+	struct list_head *func_def_pos;
+	struct init_def *init_def;
+	struct func_def *func_def;
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		init_def = list_entry(init_def_pos,
+				      struct init_def,
+				      list);
+
+		list_for_each(func_def_pos , &init_def->pre_deps)
+		{
+			func_def = list_entry(func_def_pos,
+					      struct func_def,
+					      list);
+
+			if (!strcmp(func_def->name, dep)) {
+				free(func_def->name);
+				list_del(func_def_pos);
+				free(func_def);
+				break;
+			}
+		}
+
+	}
+}
+
+
+static int add_mandatory_function(const char *name)
+{
+	struct list_head *position;
+	struct func_def *func_def;
+
+	list_for_each(position, &mandatory_functions)
+	{
+		func_def = list_entry(position, struct func_def, list);
+
+		if (!strcmp(func_def->name, name))
+			return 0;
+	}
+
+	func_def = malloc(sizeof(struct func_def));
+
+	if (!func_def)
+		return -ENOMEM;
+
+	func_def->name = strdup(name);
+
+	if (!func_def->name) {
+		free(func_def);
+		return -ENOMEM;
+	}
+
+	list_add(&func_def->list, &mandatory_functions);
+
+	return 0;
+}
+
+static int process_dep_list(struct list_head *dep_list,
+			    char *deps,
+			    int mandatory)
+{
+	int err = 0;
+	struct func_def *func_def;
+	char *save_ptr;
+
+	char *dep_function;
+
+	dep_function = strtok_r(deps, " \t", &save_ptr);
+
+	while (dep_function) {
+		func_def = malloc(sizeof(struct func_def));
+
+		if (!func_def)
+			return -ENOMEM;
+
+		func_def->name = strdup(dep_function);
+
+		if (!func_def->name) {
+			free(func_def);
+			return -ENOMEM;
+		}
+
+		list_add(&func_def->list, dep_list);
+
+		if (mandatory)
+			if (add_mandatory_function(dep_function))
+				err = 1;
+
+		dep_function = strtok_r(NULL, " ", &save_ptr);
+	};
+
+	return err;
+}
+
+static int process_init_info(struct init_def *init_def,
+			     char *deps)
+{
+	char *mandatory_deps;
+	char *pre_deps;
+	char *post_deps;
+	char *save_ptr;
+
+	INIT_LIST_HEAD(&init_def->mandatory_deps);
+	INIT_LIST_HEAD(&init_def->pre_deps);
+	INIT_LIST_HEAD(&init_def->post_deps);
+
+	mandatory_deps = strtok_r(deps, "|", &save_ptr);
+	pre_deps = strtok_r(NULL, "|", &save_ptr);
+	post_deps = strtok_r(NULL, "|", &save_ptr);
+
+	process_dep_list(&init_def->mandatory_deps, mandatory_deps, 1);
+	process_dep_list(&init_def->pre_deps, pre_deps, 0);
+	process_dep_list(&init_def->post_deps, post_deps, 0);
+
+	return 0;
+}
+
+static int check_for_duplicates(const char *function,
+				const char *group)
+{
+	struct list_head *position;
+	struct init_def *init_def;
+
+	list_for_each(position , &init_defs)
+	{
+		init_def = list_entry(position, struct init_def, list);
+
+		if (!strcmp(function, init_def->function)) {
+			fprintf(stderr,
+				"Duplicate function name '%s'\n",
+				function);
+
+			return -EEXIST;
+		}
+
+		if (!strcmp(function, init_def->group)) {
+			fprintf(stderr,
+				"Function '%s' matches an existing group\n",
+				function);
+
+			return -EEXIST;
+		}
+
+		if (!strcmp(group, init_def->function)) {
+			fprintf(stderr,
+				"Group '%s' matches an existing function\n",
+				function);
+
+			return -EEXIST;
+		}
+	}
+
+	return 0;
+}
+
+static int add_to_init_list(const char *function,
+			    const char *group,
+			    char *deps)
+{
+	int err;
+	struct init_def *init_def;
+
+	/* Check that the function is not already included */
+	err = check_for_duplicates(function, group);
+
+	if (err)
+		return err;
+
+	/* Create a list node for the new init function */
+	init_def = malloc(sizeof(struct init_def));
+
+	if (!init_def)
+		return -ENOMEM;
+
+	init_def->function = strdup(function);
+	init_def->group = strdup(group);
+	init_def->cyclic_checked = 0;
+
+	if ((!init_def->function) ||
+	    (!init_def->group)) {
+		free(init_def->function);
+		free(init_def->group);
+		free(init_def);
+		return -ENOMEM;
+	}
+
+	/* Add the new function to the init function list */
+	list_add(&init_def->list, &init_defs);
+
+	/* Process the new functions dependencies */
+	err = process_init_info(init_def, deps);
+
+	if (err)
+		return err;
+
+	return add_function_to_group(function, group);
+}
+
+static int add_to_skip_list(const char *function_name)
+{
+	struct list_head *skip_pos;
+	struct list_head *replace_pos;
+
+	struct skip_def *skip_def = NULL;
+	struct replace_def *rdef;
+
+	/* Duplicate skip definitions are OK, but we only need the fist one */
+	list_for_each(skip_pos, &skip_defs)
+	{
+		skip_def = list_entry(skip_pos, struct skip_def, list);
+
+		if (!strcmp(function_name, skip_def->name))
+			return 0;
+	}
+
+	/* Skip definitions matching a replace definition are not OK */
+	list_for_each(replace_pos, &replace_defs)
+	{
+		rdef = list_entry(replace_pos,
+				  struct replace_def,
+				  list);
+
+		if (!strcmp(skip_def->name, rdef->old_name) ||
+		    !strcmp(skip_def->name, rdef->new_name)) {
+			fprintf(stderr,
+				"Skip '%s' is in a replace definition\n",
+				skip_def->name);
+			return -EEXIST;
+		}
+	}
+
+	skip_def = malloc(sizeof(struct skip_def));
+
+	if (!skip_def)
+		return -ENOMEM;
+
+	skip_def->name = strdup(function_name);
+
+	if (!skip_def->name) {
+		free(skip_def);
+		return -ENOMEM;
+	}
+
+	list_add(&skip_def->list, &skip_defs);
+
+	return 0;
+}
+
+static int add_to_replace_list(const char *old_name, const char *new_name)
+{
+	struct list_head *skip_pos;
+	struct list_head *replace_pos;
+
+	struct skip_def *skip_def;
+	struct replace_def *rdef;
+
+	/* Duplicate replace definitions are not OK */
+	list_for_each(replace_pos , &replace_defs)
+	{
+		rdef = list_entry(replace_pos,
+				  struct replace_def,
+				  list);
+
+		if (!strcmp(old_name, rdef->old_name) ||
+		    !strcmp(old_name, rdef->new_name)) {
+			fprintf(stderr,
+				"Multiple replace defs for function '%s'\n",
+				old_name);
+
+			return -EEXIST;
+		}
+
+		if (!strcmp(new_name, rdef->old_name) ||
+		    !strcmp(new_name, rdef->new_name)) {
+			fprintf(stderr,
+				"Multiple replace defs for function '%s'\n",
+				new_name);
+
+			return -EEXIST;
+		}
+	}
+
+	/* Replace definitions matching a skip definition are not OK */
+	list_for_each(skip_pos, &skip_defs)
+	{
+		skip_def = list_entry(skip_pos, struct skip_def, list);
+
+		if (!strcmp(skip_def->name, old_name)) {
+			fprintf(stderr,
+				"Replace '%s' is in a skip definition\n",
+				old_name);
+			return -EEXIST;
+		}
+
+		if (!strcmp(skip_def->name, new_name)) {
+			fprintf(stderr,
+				"Replace '%s' is in a skip definition\n",
+				new_name);
+			return -EEXIST;
+		}
+	}
+
+	rdef = malloc(sizeof(struct replace_def));
+
+	if (!rdef)
+		return -ENOMEM;
+
+	rdef->old_name = strdup(old_name);
+	rdef->new_name = strdup(new_name);
+
+	if ((!rdef->old_name) || (!rdef->new_name)) {
+		free(rdef->old_name);
+		free(rdef->new_name);
+		free(rdef);
+		return -ENOMEM;
+	}
+
+	list_add(&rdef->list, &replace_defs);
+
+	return 0;
+}
+
+static int process_initcall_string(const char *string)
+{
+	char *save_ptr;
+	char *def_type;
+	char *function;
+	char *group;
+	char *old_name;
+	char *new_name;
+	char *deps;
+	int err;
+
+	char *local_string = strdup(string);
+
+	if (!local_string)
+		return -ENOMEM;
+
+	def_type = strtok_r(local_string, ":", &save_ptr);
+
+	switch (def_type[0]) {
+	case 'f':
+		/* An init function definition - Get the function name */
+		function = strtok_r(NULL, ":", &save_ptr);
+		group = strtok_r(NULL, ":", &save_ptr);
+		deps = strtok_r(NULL, ":", &save_ptr);
+
+		err = add_to_init_list(function, group, deps);
+		break;
+
+	case 's':
+		function = strtok_r(NULL, ":", &save_ptr);
+
+		err = add_to_skip_list(function);
+		break;
+
+	case 'r':
+		old_name = strtok_r(NULL, ":", &save_ptr);
+		new_name = strtok_r(NULL, ":", &save_ptr);
+
+		err = add_to_replace_list(old_name, new_name);
+		break;
+
+	default:
+		fprintf(stderr, "Unknown Init Type: %s", def_type);
+		err = -ENOENT;
+		break;
+	}
+
+	free(local_string);
+	return err;
+}
+
+static int process_marker_strings(void)
+{
+	int err = 0;
+
+	err = process_initcall_string(default_init_reset);
+	if (err)
+		return err;
+
+	err = process_initcall_string(default_init_sdram);
+	if (err)
+		return err;
+
+	err = process_initcall_string(default_init_reloc);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int build_function_list(char *buffer)
+{
+	int err = 0;
+
+	char *save_ptr;
+
+	char *init_call_string;
+
+	err = process_marker_strings();
+	if (err)
+		return err;
+
+	init_call_string = strtok_r(buffer, "()", &save_ptr);
+
+	while (init_call_string) {
+		if (process_initcall_string(init_call_string))
+			err = 1;
+
+		/* Skip the garbage between init definitions */
+		init_call_string = strtok_r(NULL, "(", &save_ptr);
+
+		/* Get the next init definition */
+		init_call_string = strtok_r(NULL, ")", &save_ptr);
+	};
+
+	return err;
+}
+
+static int open_file(const char *file, char **buffer, int *buf_size)
+{
+	struct stat sbuf;
+	int file_ptr;
+
+	char *ptr;
+
+	file_ptr = open(file, O_RDONLY|O_BINARY);
+
+	if (file_ptr < 0) {
+		fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno));
+		return errno;
+	}
+
+	if (fstat(file_ptr, &sbuf) < 0) {
+		fprintf(stderr, "Can't stat %s: %s\n", file, strerror(errno));
+		return errno;
+	}
+
+	ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, file_ptr, 0);
+	if (ptr == MAP_FAILED) {
+		fprintf(stderr, "Can't read %s: %s\n", file, strerror(errno));
+		return errno;
+	}
+
+	*buffer = malloc(sbuf.st_size + 1);
+
+	if (!*buffer)
+		return -ENOMEM;
+
+	*buf_size = sbuf.st_size;
+	memcpy(*buffer, ptr, sbuf.st_size);
+	(*buffer)[sbuf.st_size] = 0x00;
+
+	munmap((void *)ptr, sbuf.st_size);
+	close(file_ptr);
+
+	return 0;
+}
+
+static int check_mandatory_list(void)
+{
+	int err = 0;
+	struct list_head *position;
+	struct list_head *q;
+	struct list_head *sub_position;
+	struct init_def *init_def;
+	struct func_def *func_def;
+
+
+	/* Remove functions that exist from the mandatory functions list */
+	list_for_each_safe(position, q, &mandatory_functions) {
+		func_def = list_entry(position, struct func_def, list);
+
+		list_for_each(sub_position , &init_defs) {
+			init_def = list_entry(sub_position,
+					      struct init_def,
+					      list);
+
+			if (!strcmp(func_def->name, init_def->function) ||
+			    !strcmp(func_def->name, init_def->group)) {
+				free(func_def->name);
+
+				list_del(position);
+				free(func_def);
+			}
+		}
+
+	}
+
+	list_for_each(position , &mandatory_functions) {
+		func_def = list_entry(position, struct func_def, list);
+
+		fprintf(stderr,
+			"Missing mandatory function: %s\n",
+			func_def->name);
+		err = 1;
+	}
+
+	return err;
+}
+
+static int process_skip_list(void)
+{
+	int err = 0;
+	int group_skip = 0;
+
+	/*
+	 * The same function cannot appear in both the skip list and the
+	 * replace list (either as the new or old function name)
+	 */
+	struct list_head *skip_pos;
+	struct skip_def *skip_def;
+
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	struct list_head *q;
+
+	list_for_each(skip_pos, &skip_defs)
+	{
+		skip_def = list_entry(skip_pos, struct skip_def, list);
+
+		/* Remove the named skip from init entries */
+		list_for_each_safe(init_def_pos, q, &init_defs) {
+			init_def = list_entry(init_def_pos,
+					      struct init_def,
+					      list);
+			/*
+			 * We already know that function and group names
+			 * are unique across both namespaces so we can
+			 * delete any init definitions whose function or
+			 * group name matches the skip name
+			 */
+			if (!strcmp(init_def->group, skip_def->name))
+				group_skip = 1;
+
+			if (!strcmp(init_def->function, skip_def->name) ||
+			    group_skip) {
+				free(init_def->function);
+				free(init_def->group);
+
+				free_function_list(&init_def->mandatory_deps);
+				free_function_list(&init_def->post_deps);
+				free_function_list(&init_def->pre_deps);
+
+				list_del(init_def_pos);
+
+				free(init_def);
+			}
+		}
+
+		if (group_skip)
+			/* Remove the named skip group */
+			delete_group(skip_def->name);
+	}
+
+	return err;
+}
+
+static int process_replace_list(void)
+{
+	int err = 0;
+
+	struct list_head *init_def_pos;
+	struct list_head *replace_pos;
+
+	struct init_def *init_def;
+	struct replace_def *rdef;
+
+	struct init_group *dummy;
+
+	int group_replace;
+	int function_replace;
+
+	list_for_each(replace_pos, &replace_defs)
+	{
+		rdef = list_entry(replace_pos, struct replace_def, list);
+
+		group_replace = 0;
+		function_replace = 0;
+
+		list_for_each(init_def_pos , &init_defs)
+		{
+			init_def = list_entry(init_def_pos,
+					      struct init_def,
+					      list);
+
+			if (!strcmp(init_def->function,
+				    rdef->old_name)) {
+				/* Function replacements are easy */
+				function_replace = 1;
+
+				free(init_def->function);
+				init_def->function = strdup(rdef->new_name);
+
+				if (!init_def->function)
+					return -ENOMEM;
+
+			} else if (!strcmp(init_def->group,
+				    rdef->old_name)) {
+				/* Hmm, a group replacement */
+				group_replace = 1;
+
+				free(init_def->group);
+				init_def->group = strdup(rdef->new_name);
+
+				if (!init_def->group)
+					return -ENOMEM;
+			}
+		}
+
+		if (group_replace) {
+			/* Delete 'old name' group */
+			delete_group(rdef->old_name);
+
+			/*
+			 * Create a 'new group' (if it does not already
+			 * exist. It will be tested later and if it is
+			 * empty, an error will be reported
+			 */
+			err = find_or_create_group(rdef->new_name, &dummy);
+			if (!err)
+				return err;
+		}
+
+		if (!group_replace && !function_replace) {
+			fprintf(stderr,
+				"Replace function %s not in init list\n",
+				rdef->old_name);
+			err = ENOENT;
+		}
+	}
+
+	return err;
+}
+
+static int add_dep(const char *function, const char *dep)
+{
+	int found = 0;
+
+	struct list_head *init_def_pos;
+	struct list_head *pre_dep_pos;
+
+	struct init_def *init_def;
+	struct func_def *func_def;
+	struct func_def *pre_dep_id;
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (!strcmp(init_def->function, function)) {
+
+			list_for_each(pre_dep_pos, &init_def->pre_deps) {
+				pre_dep_id = list_entry(pre_dep_pos,
+							struct func_def,
+							list);
+				if (!strcmp(pre_dep_id->name, dep)) {
+					found = 1;
+					break;
+				}
+
+			}
+
+			if (!found) {
+				func_def = malloc(sizeof(struct func_def));
+
+				if (!func_def)
+					return -ENOMEM;
+
+				func_def->name = strdup(dep);
+
+				if (!func_def->name) {
+					free(func_def);
+					return -ENOMEM;
+				}
+
+				list_add(&func_def->list, &init_def->pre_deps);
+			}
+
+			return 0;
+		}
+	}
+
+	fprintf(stderr, "Function '%s' not found\n", function);
+	return -ENOENT;
+}
+
+
+static int insert_into_dep_list(struct list_head *dep_list,
+				struct init_group *init_group)
+{
+	struct list_head *position;
+	struct func_def *func_def;
+	struct func_def *new_func_def;
+
+	list_for_each(position, &init_group->functions)
+	{
+		func_def = list_entry(position, struct func_def, list);
+
+		new_func_def = malloc(sizeof(struct func_def));
+
+		if (!new_func_def)
+			return -ENOMEM;
+
+		new_func_def->name = strdup(func_def->name);
+
+		if (!new_func_def->name) {
+			free(new_func_def);
+			return -ENOMEM;
+		}
+
+		list_add(&new_func_def->list, dep_list);
+	}
+
+	return 0;
+}
+
+static int expand_dep_list(struct list_head *dep_list)
+{
+	int err = 0;
+
+	struct list_head *position;
+	struct func_def *func_def;
+	struct list_head *q;
+
+	struct list_head *step_pos;
+	struct init_group *init_group;
+
+
+	list_for_each_safe(position, q, dep_list) {
+		func_def = list_entry(position, struct func_def, list);
+
+		/* Is this a 'step' rather than a 'function' */
+		list_for_each(step_pos, &init_groups)
+		{
+			init_group = list_entry(step_pos,
+						struct init_group,
+						list);
+
+			if (!strcmp(init_group->name, func_def->name)) {
+				/*
+				 * Replace this init id (which is a 'step'
+				 * with the list of step functions
+				 */
+				free(func_def->name);
+
+				list_del(position);
+				free(func_def);
+
+				err = insert_into_dep_list(dep_list,
+							   init_group);
+
+				if (err)
+					return err;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int expand_dep_lists(void)
+{
+	int err = 0;
+
+	struct list_head *position;
+	struct init_def *init_def;
+
+	list_for_each(position , &init_defs)
+	{
+		init_def = list_entry(position, struct init_def, list);
+
+		err = expand_dep_list(&init_def->mandatory_deps);
+
+		if (err)
+			return err;
+
+		err = expand_dep_list(&init_def->pre_deps);
+
+		if (err)
+			return err;
+
+		err = expand_dep_list(&init_def->post_deps);
+
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+
+static void remove_unused_deps(struct list_head *list,
+			       const char *function,
+			       const char *dep_type)
+{
+	struct list_head *dep_pos;
+	struct func_def *func_def;
+	struct list_head *q;
+
+	list_for_each_safe(dep_pos, q, list)
+	{
+		func_def = list_entry(dep_pos,
+				     struct func_def,
+				     list);
+
+		if (!function_exists(func_def->name)) {
+			fprintf(stderr,
+				"Removing unmet %s dep '%s' from '%s'\n",
+				dep_type,
+				func_def->name,
+				function);
+			free(func_def->name);
+			list_del(dep_pos);
+			free(func_def);
+		}
+	}
+
+}
+
+static int strip_unmet_deps(void)
+{
+	/*
+	 * Any entry in the pre-dep and post-dep lists that do not have a
+	 * matching entry in the init_defs list can be removed
+	 */
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		remove_unused_deps(&init_def->pre_deps,
+				   init_def->function,
+				   "pre");
+		remove_unused_deps(&init_def->post_deps,
+				   init_def->function,
+				   "post");
+	}
+
+	return 0;
+}
+
+static int cleanup_dep_lists(void)
+{
+	int err = 0;
+	int found;
+
+	struct list_head *init_def_pos;
+	struct list_head *dep_pos;
+	struct list_head *pre_dep_pos;
+
+	struct list_head *q;
+
+	struct init_def *init_def;
+	struct func_def *func_def;
+	struct func_def *pre_dep_id;
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		/* Convert post-deps into pre-deps */
+		list_for_each_safe(dep_pos, q, &init_def->post_deps)
+		{
+			func_def = list_entry(dep_pos,
+					      struct func_def,
+					      list);
+
+			err = add_dep(func_def->name, init_def->function);
+			if (err)
+				return err;
+
+			list_del(dep_pos);
+
+			free(func_def->name);
+			free(func_def);
+		}
+
+		/* Move mandatory deps into pre-deps list */
+		list_for_each_safe(dep_pos, q, &init_def->mandatory_deps)
+		{
+			func_def = list_entry(dep_pos,
+					      struct func_def,
+					      list);
+
+			list_del(dep_pos);
+
+			found = 0;
+
+			list_for_each(pre_dep_pos, &init_def->pre_deps)
+			{
+				pre_dep_id = list_entry(pre_dep_pos,
+							struct func_def,
+							list);
+
+				if (!strcmp(func_def->name, pre_dep_id->name)) {
+					found = 1;
+					break;
+				}
+
+			}
+
+			if (!found) {
+				list_add(&func_def->list,
+					 &init_def->pre_deps);
+			} else {
+				free(func_def->name);
+				free(func_def);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int check_unique_root_function(void)
+{
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	int root_functions = 0;
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (list_empty(&init_def->pre_deps))
+			root_functions++;
+	}
+
+	if (root_functions != 1)
+		return -EINVAL;
+	else
+		return 0;
+}
+
+static void dump_dep_stack(struct list_head *dep_stack, const char *function)
+{
+	struct list_head *func_def_pos;
+	struct func_def *func_def;
+
+	list_for_each(func_def_pos , dep_stack) {
+		func_def = list_entry(func_def_pos, struct func_def, list);
+
+		fprintf(stderr, "  %s\n", func_def->name);
+
+		if (!strcmp(func_def->name, function))
+			return;
+	}
+}
+
+static struct init_def *find_init_def_by_function(const char *function)
+{
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (!strcmp(init_def->function, function))
+			return init_def;
+	}
+
+	fprintf(stderr, "Cannot find %s\n", function);
+
+	return NULL;
+}
+
+static int check_init_def_cyclic(struct init_def *init_def,
+				 struct list_head *dep_stack)
+{
+	int err = 0;
+
+	struct list_head *func_def_pos;
+	struct func_def *func_def;
+
+	struct list_head *dep_init_func_pos;
+	struct func_def *dep_init_func;
+	struct init_def *dep_init_def;
+
+	if (init_def->cyclic_checked)
+		return 0;
+
+	/* Check if this function already in the dependency stack */
+	list_for_each(func_def_pos , dep_stack) {
+		func_def = list_entry(func_def_pos, struct func_def, list);
+
+		if (!strcmp(init_def->function, func_def->name)) {
+			fprintf(stderr,
+				"Function '%s' has cyclic dependency\n",
+				init_def->function);
+
+			/* Dump the dependency stack */
+			dump_dep_stack(dep_stack, init_def->function);
+
+			init_def->cyclic_checked = 1;
+			return -EEXIST;
+		}
+	}
+
+	/* Add this function to the check stack */
+	func_def = malloc(sizeof(struct func_def));
+
+	if (!func_def)
+		return -ENOMEM;
+
+	func_def->name = strdup(init_def->function);
+
+	if (!func_def->name) {
+		free(func_def);
+		return -ENOMEM;
+	}
+
+	list_add(&func_def->list, dep_stack);
+
+	/* Now check the all the dependencies of this function */
+	list_for_each(dep_init_func_pos , &init_def->pre_deps)
+	{
+		dep_init_func = list_entry(dep_init_func_pos,
+					   struct func_def,
+					   list);
+
+		dep_init_def = find_init_def_by_function(dep_init_func->name);
+
+		if (!dep_init_def)
+			return 1;
+
+		if (check_init_def_cyclic(dep_init_def, dep_stack))
+			err = 1;
+	}
+
+	init_def->cyclic_checked = 1;
+
+	return err;
+}
+
+static void clear_dep_stack(struct list_head *dep_stack)
+{
+	struct list_head *func_def_pos;
+	struct list_head *q;
+	struct func_def *func_def;
+
+	list_for_each_safe(func_def_pos, q, dep_stack) {
+		func_def = list_entry(func_def_pos,
+				      struct func_def,
+				      list);
+
+		list_del(func_def_pos);
+		free(func_def->name);
+		free(func_def);
+	}
+}
+
+static int check_for_cyclic_deps(void)
+{
+	int err = 0;
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	struct list_head dep_stack;
+
+
+	INIT_LIST_HEAD(&dep_stack);
+
+	list_for_each(init_def_pos , &init_defs)
+	{
+		clear_dep_stack(&dep_stack);
+
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (!init_def->cyclic_checked)
+			if (check_init_def_cyclic(init_def, &dep_stack))
+				err = 1;
+	}
+
+	clear_dep_stack(&dep_stack);
+
+	return err;
+}
+
+int generate_init_sequence(void)
+{
+	struct init_def *init_def;
+
+	while (!list_empty(&init_defs)) {
+		init_def = pop_first_independent_function();
+
+		if (!init_def) {
+			fprintf(stderr, "Cyclic deps\n");
+			return 0;
+		}
+
+		list_add_tail(&init_def->list, &init_sequence);
+
+		delete_dep_from_init_defs(init_def->function);
+	}
+
+	return 0;
+}
+
+int generate_c_file(const char *file)
+{
+	struct list_head *init_def_pos;
+	struct init_def *init_def;
+
+	FILE *file_ptr = fopen(file, "w");
+
+	if (!file_ptr) {
+		fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno));
+		return errno;
+	}
+
+	fputs("/*\n", file_ptr);
+	fputs(" * DO NOT MODIFY.\n", file_ptr);
+	fputs(" *\n", file_ptr);
+	fputs(" * This file was generated by mkinitseq\n", file_ptr);
+	fputs(" *\n", file_ptr);
+	fputs(" */\n", file_ptr);
+	fputs("\n", file_ptr);
+	fputs("#include <init_func.h>\n", file_ptr);
+	fputs("#include <linux/stddef.h>\n", file_ptr);
+	fputs("\n", file_ptr);
+
+	list_for_each(init_def_pos , &init_sequence)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (strcmp(init_def->function, "RESET") &
+		    strcmp(init_def->function, "SDRAM") &
+		    strcmp(init_def->function, "RELOC")) {
+			fprintf(file_ptr, "int %s(void);\n",
+				init_def->function);
+		}
+	}
+
+	list_for_each(init_def_pos , &init_sequence)
+	{
+		init_def = list_entry(init_def_pos, struct init_def, list);
+
+		if (!strcmp(init_def->function, "RESET")) {
+			fputs("init_fnc_t *init_sequence_f[] = {\n",
+			      file_ptr);
+		} else if (!strcmp(init_def->function, "SDRAM")) {
+			fputs("\n", file_ptr);
+			fputs("\t\tNULL,\n", file_ptr);
+			fputs("\t};\n", file_ptr);
+
+			fputs("\n", file_ptr);
+			fputs("init_fnc_t *init_sequence_f_r[] = {\n",
+			      file_ptr);
+		} else if (!strcmp(init_def->function, "RELOC")) {
+			fputs("\n", file_ptr);
+			fputs("\t\tNULL,\n", file_ptr);
+			fputs("\t};\n", file_ptr);
+
+			fputs("\n", file_ptr);
+			fputs("init_fnc_t *init_sequence_r[] = {\n",
+			      file_ptr);
+		} else {
+			fprintf(file_ptr, "\t\t%s,\n", init_def->function);
+		}
+	}
+
+	fputs("\n", file_ptr);
+	fputs("\t\tNULL,\n", file_ptr);
+	fputs("\t};\n", file_ptr);
+
+	fclose(file_ptr);
+
+	return 0;
+}
+
+void init_lists(void)
+{
+	INIT_LIST_HEAD(&init_defs);
+	INIT_LIST_HEAD(&skip_defs);
+	INIT_LIST_HEAD(&replace_defs);
+
+	INIT_LIST_HEAD(&mandatory_functions);
+	INIT_LIST_HEAD(&init_groups);
+
+	INIT_LIST_HEAD(&init_sequence);
+}
+
+int main(int argc, const char **argv)
+{
+	int err;
+	int buf_size = 0;
+	char *local_buffer = NULL;
+	char *x;
+
+	printf("Generating init sequence from %s\n", argv[1]);
+
+	/* Read the init function definitions into a local buffer */
+	err = open_file(argv[1], &local_buffer, &buf_size);
+
+	if (err || !local_buffer)
+		exit(EXIT_FAILURE);
+
+	/*
+	 * Convert all the NULLs (except the last one) to non-NULL so
+	 * the buffer can be processed using standard string functions
+	 */
+	for (x = local_buffer; x != &local_buffer[buf_size]; x++) {
+		if (*x == 0x00)
+			*x = 0x01;
+	}
+
+	init_lists();
+
+	if (build_function_list(local_buffer))
+		exit(EXIT_FAILURE);
+
+	if (check_mandatory_list())
+		exit(EXIT_FAILURE);
+
+	if (process_skip_list())
+		exit(EXIT_FAILURE);
+
+	if (process_replace_list())
+		exit(EXIT_FAILURE);
+
+	if (check_for_empty_groups())
+		exit(EXIT_FAILURE);
+
+	if (expand_dep_lists())
+		exit(EXIT_FAILURE);
+
+	if (strip_unmet_deps())
+		exit(EXIT_FAILURE);
+
+	if (cleanup_dep_lists())
+		exit(EXIT_FAILURE);
+
+	if (check_unique_root_function())
+		exit(EXIT_FAILURE);
+
+	if (check_for_cyclic_deps())
+		exit(EXIT_FAILURE);
+
+	if (generate_init_sequence())
+		exit(EXIT_FAILURE);
+
+	if (generate_c_file(argv[2]))
+		exit(EXIT_FAILURE);
+
+	free(local_buffer);
+	exit(EXIT_SUCCESS);
+}
-- 
1.7.7.6



More information about the U-Boot mailing list