From patchwork Sun Apr 22 15:23:06 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Graeme Russ X-Patchwork-Id: 154286 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id EF70BB6FC8 for ; Mon, 23 Apr 2012 01:23:54 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 15CAA2819F; Sun, 22 Apr 2012 17:23:45 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id w+KRxUbwBMo0; Sun, 22 Apr 2012 17:23:44 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id DE19C28194; Sun, 22 Apr 2012 17:23:35 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CA3C92812F for ; Sun, 22 Apr 2012 17:23:28 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 3c2nO4oPyAxH for ; Sun, 22 Apr 2012 17:23:26 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pb0-f44.google.com (mail-pb0-f44.google.com [209.85.160.44]) by theia.denx.de (Postfix) with ESMTPS id 1B35828178 for ; Sun, 22 Apr 2012 17:23:22 +0200 (CEST) Received: by pbbrp16 with SMTP id rp16so2409214pbb.3 for ; Sun, 22 Apr 2012 08:23:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=JQO4lLoEsBSFzqO9nmxX+Xe35kJ/QxAy9oDg+Fm9UfQ=; b=oecs6gC6QqBCO7fAgHu5H/7t+BHFV6VlG5y25cD0ehAWX7sC5SlqfIJmNHlJ7TkgoR 6+/e63G1DUMojLdi47Som3G6tJf9ehxmMfW2LAOp4jHwA0TT4XytywYgV2tM9vQyi/p8 kO2Odrc5t9/d4gbvwLT4Vu9INrySwpWtiClplFIhMvJvvdPssIUaOAQ+cyEg8npgZWST g3JKxmaONyl3bWRF1NVp3Q9+rmRgUDB6I2pHvIHa/8eltIXqFJr6prQIjoFhLUr2yRDP 1OlsmK2xqdLAyCiwPosfVcvFcWKoAR6rMo4iPk8ENA/gguHXzykGD6BxYdv35MTkH5xG lr7Q== Received: by 10.68.200.225 with SMTP id jv1mr16312466pbc.120.1335108199954; Sun, 22 Apr 2012 08:23:19 -0700 (PDT) Received: from dingo.localhost (d110-32-170-80.sbr801.nsw.optusnet.com.au. [110.32.170.80]) by mx.google.com with ESMTPS id wn3sm11636628pbc.74.2012.04.22.08.23.17 (version=SSLv3 cipher=OTHER); Sun, 22 Apr 2012 08:23:19 -0700 (PDT) From: Graeme Russ To: u-boot@lists.denx.de Date: Mon, 23 Apr 2012 01:23:06 +1000 Message-Id: <1335108188-21875-2-git-send-email-graeme.russ@gmail.com> X-Mailer: git-send-email 1.7.7.6 In-Reply-To: <1335108188-21875-1-git-send-email-graeme.russ@gmail.com> References: <1335108188-21875-1-git-send-email-graeme.russ@gmail.com> Subject: [U-Boot] [PATCH 1/3] init_func: Add fundamental framework X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Signed-off-by: Graeme Russ --- 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 + +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 \n", file_ptr); + fputs("#include \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); +}