Patchwork [U-Boot,RFC] Replace clunky init sequence architecture

login
register
mail settings
Submitter Graeme Russ
Date Aug. 18, 2011, 12:34 p.m.
Message ID <1313670862-31216-1-git-send-email-graeme.russ@gmail.com>
Download mbox | patch
Permalink /patch/110515/
State Superseded
Headers show

Comments

Graeme Russ - Aug. 18, 2011, 12:34 p.m.
NOTE: This is an x86 only patch - I didn't include x86 in the patch heading
because it is a proof of concept for a global patch - Sorry for the blatant
etiquette violation ;)

Replace the init_fnc_t *init_sequence[] style init sequence with one based
on the Linux __initcall macros.

Functions are declared as initialisation functions by using the new
INIT_FUNC() macro. The three parameters to INIT_FUNC() are:
 - Type - f = pre-relocation (flash), r = post-relocation (RAM)
 - Sequence - Lower numbers are run first
 - Function - The function to call during the init sequence

 INIT_FUNC() creates a static variable which is a pointer to the init
 function to be called. Each variable is placed in a sub-section under
 the .initfuncs_f or .initfuncs_r sections. The sub-section name is the
 sequence number. There is no problem giving multiple functions the same
 sequence number, but the order of execution of functions with the same
 sequence number is undefined. The resulting raw image layout is a
 sequence of function pointers (much like how the command table is built)

 do_init_loop() simply takes a pointer to the start and end of either the
 initfuncs_f or initfuncs_r sections and sequentially dereferences the
 function pointer and calls the function.

 For a board to add an arbitrary initialisation function is trivial -
 simply add a INIT_FUNC() entry with an appropriate sequence number

 I imagine the sequence numbers could be #defined, but I don't exactly
 now what the linker will do - I use leading zeros in the sequence numbers
 to ensure correct ordering.

 This has been build and run-tested on my eNET board and works perfectly
 (after a few false starts)

 Enjoy :)

---
 arch/x86/cpu/cpu.c               |    1 +
 arch/x86/cpu/sc520/sc520.c       |    1 +
 arch/x86/cpu/sc520/sc520_sdram.c |    2 +
 arch/x86/cpu/sc520/sc520_timer.c |    1 +
 arch/x86/cpu/u-boot.lds          |   10 ++++++
 arch/x86/lib/board.c             |   66 ++++++++++++++++----------------------
 arch/x86/lib/pcat_interrupts.c   |    1 +
 board/eNET/eNET.c                |    2 +
 common/console.c                 |    1 +
 common/env_flash.c               |    2 +
 common/serial.c                  |    1 +
 include/common.h                 |    8 ++++
 12 files changed, 58 insertions(+), 38 deletions(-)

--
1.7.5.2.317.g391b14
Mike Frysinger - Aug. 18, 2011, 2:37 p.m.
On Thursday, August 18, 2011 08:34:22 Graeme Russ wrote:
> Replace the init_fnc_t *init_sequence[] style init sequence with one based
> on the Linux __initcall macros.

i like the idea of decentralizing the init sequence out of the arches

> Functions are declared as initialisation functions by using the new
> INIT_FUNC() macro. The three parameters to INIT_FUNC() are:
>  - Type - f = pre-relocation (flash), r = post-relocation (RAM)
>  - Sequence - Lower numbers are run first
>  - Function - The function to call during the init sequence

in this scenario, we'll have to clearly document the #'s and what is expected 
to run at each so there's no confusion
-mike
Simon Glass - Aug. 18, 2011, 4:41 p.m.
Hi Graeme,

This looks great to me.

- perhaps INIT_FUNC() for post-relocation ones, have a separate
INIT_FUNC_F or INIT_FUNC_PRE_RELOC for the others (which should be
uncommon).

- I wonder if do_init_loop could panic and pass as message the
function address which failed, or perhaps return that information?

Regards
Simon

On Thu, Aug 18, 2011 at 5:34 AM, Graeme Russ <graeme.russ@gmail.com> wrote:
> NOTE: This is an x86 only patch - I didn't include x86 in the patch heading
> because it is a proof of concept for a global patch - Sorry for the blatant
> etiquette violation ;)
>
> Replace the init_fnc_t *init_sequence[] style init sequence with one based
> on the Linux __initcall macros.
>
> Functions are declared as initialisation functions by using the new
> INIT_FUNC() macro. The three parameters to INIT_FUNC() are:
>  - Type - f = pre-relocation (flash), r = post-relocation (RAM)
>  - Sequence - Lower numbers are run first
>  - Function - The function to call during the init sequence
>
>  INIT_FUNC() creates a static variable which is a pointer to the init
>  function to be called. Each variable is placed in a sub-section under
>  the .initfuncs_f or .initfuncs_r sections. The sub-section name is the
>  sequence number. There is no problem giving multiple functions the same
>  sequence number, but the order of execution of functions with the same
>  sequence number is undefined. The resulting raw image layout is a
>  sequence of function pointers (much like how the command table is built)
>
>  do_init_loop() simply takes a pointer to the start and end of either the
>  initfuncs_f or initfuncs_r sections and sequentially dereferences the
>  function pointer and calls the function.
>
>  For a board to add an arbitrary initialisation function is trivial -
>  simply add a INIT_FUNC() entry with an appropriate sequence number
>
>  I imagine the sequence numbers could be #defined, but I don't exactly
>  now what the linker will do - I use leading zeros in the sequence numbers
>  to ensure correct ordering.
>
>  This has been build and run-tested on my eNET board and works perfectly
>  (after a few false starts)
>
>  Enjoy :)
>
> ---
>  arch/x86/cpu/cpu.c               |    1 +
>  arch/x86/cpu/sc520/sc520.c       |    1 +
>  arch/x86/cpu/sc520/sc520_sdram.c |    2 +
>  arch/x86/cpu/sc520/sc520_timer.c |    1 +
>  arch/x86/cpu/u-boot.lds          |   10 ++++++
>  arch/x86/lib/board.c             |   66 ++++++++++++++++----------------------
>  arch/x86/lib/pcat_interrupts.c   |    1 +
>  board/eNET/eNET.c                |    2 +
>  common/console.c                 |    1 +
>  common/env_flash.c               |    2 +
>  common/serial.c                  |    1 +
>  include/common.h                 |    8 ++++
>  12 files changed, 58 insertions(+), 38 deletions(-)
>
> diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
> index cac12c0..610b8d8 100644
> --- a/arch/x86/cpu/cpu.c
> +++ b/arch/x86/cpu/cpu.c
> @@ -119,6 +119,7 @@ int x86_cpu_init_r(void)
>        return 0;
>  }
>  int cpu_init_r(void) __attribute__((weak, alias("x86_cpu_init_r")));
> +INIT_FUNC(r, 010, cpu_init_r);
>
>  int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
>  {
> diff --git a/arch/x86/cpu/sc520/sc520.c b/arch/x86/cpu/sc520/sc520.c
> index e37c403..fc2996a 100644
> --- a/arch/x86/cpu/sc520/sc520.c
> +++ b/arch/x86/cpu/sc520/sc520.c
> @@ -53,6 +53,7 @@ int cpu_init_f(void)
>
>        return x86_cpu_init_f();
>  }
> +INIT_FUNC(f, 010, cpu_init_f);
>
>  int cpu_init_r(void)
>  {
> diff --git a/arch/x86/cpu/sc520/sc520_sdram.c b/arch/x86/cpu/sc520/sc520_sdram.c
> index f3623f5..5f836f3 100644
> --- a/arch/x86/cpu/sc520/sc520_sdram.c
> +++ b/arch/x86/cpu/sc520/sc520_sdram.c
> @@ -57,6 +57,7 @@ int dram_init_f(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 070, dram_init_f);
>
>  static inline void sc520_dummy_write(void)
>  {
> @@ -530,3 +531,4 @@ int dram_init(void)
>
>        return 0;
>  }
> +INIT_FUNC(r, 030, dram_init);
> diff --git a/arch/x86/cpu/sc520/sc520_timer.c b/arch/x86/cpu/sc520/sc520_timer.c
> index 5cccda1..56a53bc 100644
> --- a/arch/x86/cpu/sc520/sc520_timer.c
> +++ b/arch/x86/cpu/sc520/sc520_timer.c
> @@ -69,6 +69,7 @@ int timer_init(void)
>
>        return 0;
>  }
> +INIT_FUNC(r, 050, timer_init);
>
>  /* Allow boards to override udelay implementation */
>  void __udelay(unsigned long usec)
> diff --git a/arch/x86/cpu/u-boot.lds b/arch/x86/cpu/u-boot.lds
> index fe28030..7b2ab7a 100644
> --- a/arch/x86/cpu/u-boot.lds
> +++ b/arch/x86/cpu/u-boot.lds
> @@ -54,6 +54,16 @@ SECTIONS
>        .got : { *(.got*) }
>
>        . = ALIGN(4);
> +       __initfuncs_r_start = .;
> +       .initfuncs_r : { KEEP(*(SORT_BY_NAME(.initfuncs_r*))) }
> +       __initfuncs_r_end = .;
> +
> +       . = ALIGN(4);
> +       __initfuncs_f_start = .;
> +       .initfuncs_f : { KEEP(*(SORT_BY_NAME(.initfuncs_f*))) }
> +       __initfuncs_f_end = .;
> +
> +       . = ALIGN(4);
>        __data_end = .;
>
>        . = ALIGN(4);
> diff --git a/arch/x86/lib/board.c b/arch/x86/lib/board.c
> index b1b8680..e023f58 100644
> --- a/arch/x86/lib/board.c
> +++ b/arch/x86/lib/board.c
> @@ -64,6 +64,10 @@ extern ulong __rel_dyn_start;
>  extern ulong __rel_dyn_end;
>  extern ulong __bss_start;
>  extern ulong __bss_end;
> +extern ulong __initfuncs_f_start;
> +extern ulong __initfuncs_f_end;
> +extern ulong __initfuncs_r_start;
> +extern ulong __initfuncs_r_end;
>
>  /************************************************************************
>  * Init Utilities                                                      *
> @@ -83,6 +87,7 @@ static int init_baudrate (void)
>
>        return (0);
>  }
> +INIT_FUNC(f, 040, init_baudrate);
>
>  static int display_banner (void)
>  {
> @@ -101,6 +106,7 @@ static int display_banner (void)
>
>        return (0);
>  }
> +INIT_FUNC(r, 060, display_banner);
>
>  static int display_dram_config (void)
>  {
> @@ -115,6 +121,7 @@ static int display_dram_config (void)
>
>        return (0);
>  }
> +INIT_FUNC(r, 070, display_dram_config);
>
>  static void display_flash_config (ulong size)
>  {
> @@ -152,34 +159,6 @@ static int copy_uboot_to_ram(void);
>  static int clear_bss(void);
>  static int do_elf_reloc_fixups(void);
>
> -init_fnc_t *init_sequence_f[] = {
> -       cpu_init_f,
> -       board_early_init_f,
> -       env_init,
> -       init_baudrate,
> -       serial_init,
> -       console_init_f,
> -       dram_init_f,
> -       calculate_relocation_address,
> -       copy_uboot_to_ram,
> -       clear_bss,
> -       do_elf_reloc_fixups,
> -
> -       NULL,
> -};
> -
> -init_fnc_t *init_sequence_r[] = {
> -       cpu_init_r,             /* basic cpu dependent setup */
> -       board_early_init_r,     /* basic board dependent setup */
> -       dram_init,              /* configure available RAM banks */
> -       interrupt_init,         /* set up exceptions */
> -       timer_init,
> -       display_banner,
> -       display_dram_config,
> -
> -       NULL,
> -};
> -
>  gd_t *gd;
>
>  static int calculate_relocation_address(void)
> @@ -201,6 +180,7 @@ static int calculate_relocation_address(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 080, calculate_relocation_address);
>
>  static int copy_uboot_to_ram(void)
>  {
> @@ -213,6 +193,7 @@ static int copy_uboot_to_ram(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 090, copy_uboot_to_ram);
>
>  static int clear_bss(void)
>  {
> @@ -227,6 +208,7 @@ static int clear_bss(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 100, clear_bss);
>
>  static int do_elf_reloc_fixups(void)
>  {
> @@ -241,18 +223,25 @@ static int do_elf_reloc_fixups(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 110, do_elf_reloc_fixups);
> +
> +static void do_init_loop(init_fnc_t **fnc, init_fnc_t **end)
> +{
> +       do {
> +               if ((*fnc)() != 0)
> +                               hang();
> +       } while (fnc++ < end);
> +}
>
>  /* Load U-Boot into RAM, initialize BSS, perform relocation adjustments */
>  void board_init_f(ulong boot_flags)
>  {
> -       init_fnc_t **init_fnc_ptr;
> +       init_fnc_t **init_fnc_start = (init_fnc_t **)(&__initfuncs_f_start);
> +       init_fnc_t **init_fnc_end = (init_fnc_t **)(&__initfuncs_f_end);
>
>        gd->flags = boot_flags;
>
> -       for (init_fnc_ptr = init_sequence_f; *init_fnc_ptr; ++init_fnc_ptr) {
> -               if ((*init_fnc_ptr)() != 0)
> -                       hang();
> -       }
> +       do_init_loop(init_fnc_start, --init_fnc_end);
>
>        gd->flags |= GD_FLG_RELOC;
>
> @@ -269,7 +258,9 @@ void board_init_r(gd_t *id, ulong dest_addr)
>        ulong size;
>        static bd_t bd_data;
>        static gd_t gd_data;
> -       init_fnc_t **init_fnc_ptr;
> +
> +       init_fnc_t **init_fnc_start = (init_fnc_t **)(&__initfuncs_r_start);
> +       init_fnc_t **init_fnc_end = (init_fnc_t **)(&__initfuncs_r_end);
>
>        show_boot_progress(0x21);
>
> @@ -289,12 +280,11 @@ void board_init_r(gd_t *id, ulong dest_addr)
>        mem_malloc_init((((ulong)dest_addr - CONFIG_SYS_MALLOC_LEN)+3)&~3,
>                        CONFIG_SYS_MALLOC_LEN);
>
> -       for (init_fnc_ptr = init_sequence_r; *init_fnc_ptr; ++init_fnc_ptr) {
> -               if ((*init_fnc_ptr)() != 0)
> -                       hang ();
> -       }
> +       do_init_loop(init_fnc_start, --init_fnc_end);
>        show_boot_progress(0x23);
>
> +
> +
>  #ifdef CONFIG_SERIAL_MULTI
>        serial_initialize();
>  #endif
> diff --git a/arch/x86/lib/pcat_interrupts.c b/arch/x86/lib/pcat_interrupts.c
> index 2caae20..46514b0 100644
> --- a/arch/x86/lib/pcat_interrupts.c
> +++ b/arch/x86/lib/pcat_interrupts.c
> @@ -82,6 +82,7 @@ int interrupt_init(void)
>
>        return 0;
>  }
> +INIT_FUNC(r, 040, interrupt_init);
>
>  void mask_irq(int irq)
>  {
> diff --git a/board/eNET/eNET.c b/board/eNET/eNET.c
> index 2a5636c..f49c3a5 100644
> --- a/board/eNET/eNET.c
> +++ b/board/eNET/eNET.c
> @@ -106,6 +106,7 @@ int board_early_init_f(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 020, board_early_init_f);
>
>  static void enet_setup_pars(void)
>  {
> @@ -161,6 +162,7 @@ int board_early_init_r(void)
>
>        return 0;
>  }
> +INIT_FUNC(r, 020, board_early_init_r);
>
>  void show_boot_progress(int val)
>  {
> diff --git a/common/console.c b/common/console.c
> index 8c650e0..559c799 100644
> --- a/common/console.c
> +++ b/common/console.c
> @@ -531,6 +531,7 @@ int console_init_f(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 060, console_init_f);
>
>  void stdio_print_current_devices(void)
>  {
> diff --git a/common/env_flash.c b/common/env_flash.c
> index 50ca4ffa..a63c108 100644
> --- a/common/env_flash.c
> +++ b/common/env_flash.c
> @@ -126,6 +126,7 @@ int  env_init(void)
>
>        return 0;
>  }
> +INIT_FUNC(f, 030, env_init);
>
>  #ifdef CMD_SAVEENV
>  int saveenv(void)
> @@ -252,6 +253,7 @@ int  env_init(void)
>        gd->env_valid = 0;
>        return 0;
>  }
> +INIT_FUNC(f, 030, env_init);
>
>  #ifdef CMD_SAVEENV
>
> diff --git a/common/serial.c b/common/serial.c
> index 995d268..5d097d8 100644
> --- a/common/serial.c
> +++ b/common/serial.c
> @@ -168,6 +168,7 @@ int serial_init (void)
>
>        return serial_current->init ();
>  }
> +INIT_FUNC(f, 050, serial_init);
>
>  void serial_setbrg (void)
>  {
> diff --git a/include/common.h b/include/common.h
> index 12a1074..61126f1 100644
> --- a/include/common.h
> +++ b/include/common.h
> @@ -39,8 +39,16 @@ typedef volatile unsigned char       vu_char;
>  #include <linux/bitops.h>
>  #include <linux/types.h>
>  #include <linux/string.h>
> +#include <linux/compiler.h>
>  #include <asm/ptrace.h>
>  #include <stdarg.h>
> +
> +typedef int (*initfunc_t)(void);
> +
> +#define INIT_FUNC(stage,step,fn) \
> +       static initfunc_t __initfunc_ ## fn ## stage __used \
> +       __attribute__((__section__(".initfuncs_" #stage "." #step))) = fn
> +
>  #if defined(CONFIG_PCI) && (defined(CONFIG_4xx) && !defined(CONFIG_AP1000))
>  #include <pci.h>
>  #endif
> --
> 1.7.5.2.317.g391b14
>
>
Wolfgang Denk - Aug. 22, 2011, 8:10 p.m.
Dear Graeme Russ,

In message <1313670862-31216-1-git-send-email-graeme.russ@gmail.com> you wrote:
> NOTE: This is an x86 only patch - I didn't include x86 in the patch heading
> because it is a proof of concept for a global patch - Sorry for the blatant
> etiquette violation ;)

Don't worry. For an RFC tyhis is OK.

> Replace the init_fnc_t *init_sequence[] style init sequence with one based
> on the Linux __initcall macros.
> 
> Functions are declared as initialisation functions by using the new
> INIT_FUNC() macro. The three parameters to INIT_FUNC() are:
>  - Type - f = pre-relocation (flash), r = post-relocation (RAM)
>  - Sequence - Lower numbers are run first
>  - Function - The function to call during the init sequence

For general remarks please see my previous comment to your older
posting about this topic here:
http://article.gmane.org/gmane.comp.boot-loaders.u-boot/105824


>  For a board to add an arbitrary initialisation function is trivial -
>  simply add a INIT_FUNC() entry with an appropriate sequence number

This is actually hte biffest voncern I have: I cannot imagine how you
will try to figure out the exact init sequence when writing the code
(or, even worse, when reading somebody else's code).

>  I imagine the sequence numbers could be #defined, but I don't exactly
>  now what the linker will do - I use leading zeros in the sequence numbers
>  to ensure correct ordering.

This is another concern: what exactly will the linker do, and
different versions of linkers with differen options and/or levels of
optimization?

Best regards,

Wolfgang Denk

Patch

diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index cac12c0..610b8d8 100644
--- a/arch/x86/cpu/cpu.c
+++ b/arch/x86/cpu/cpu.c
@@ -119,6 +119,7 @@  int x86_cpu_init_r(void)
 	return 0;
 }
 int cpu_init_r(void) __attribute__((weak, alias("x86_cpu_init_r")));
+INIT_FUNC(r, 010, cpu_init_r);

 int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
diff --git a/arch/x86/cpu/sc520/sc520.c b/arch/x86/cpu/sc520/sc520.c
index e37c403..fc2996a 100644
--- a/arch/x86/cpu/sc520/sc520.c
+++ b/arch/x86/cpu/sc520/sc520.c
@@ -53,6 +53,7 @@  int cpu_init_f(void)

 	return x86_cpu_init_f();
 }
+INIT_FUNC(f, 010, cpu_init_f);

 int cpu_init_r(void)
 {
diff --git a/arch/x86/cpu/sc520/sc520_sdram.c b/arch/x86/cpu/sc520/sc520_sdram.c
index f3623f5..5f836f3 100644
--- a/arch/x86/cpu/sc520/sc520_sdram.c
+++ b/arch/x86/cpu/sc520/sc520_sdram.c
@@ -57,6 +57,7 @@  int dram_init_f(void)

 	return 0;
 }
+INIT_FUNC(f, 070, dram_init_f);

 static inline void sc520_dummy_write(void)
 {
@@ -530,3 +531,4 @@  int dram_init(void)

 	return 0;
 }
+INIT_FUNC(r, 030, dram_init);
diff --git a/arch/x86/cpu/sc520/sc520_timer.c b/arch/x86/cpu/sc520/sc520_timer.c
index 5cccda1..56a53bc 100644
--- a/arch/x86/cpu/sc520/sc520_timer.c
+++ b/arch/x86/cpu/sc520/sc520_timer.c
@@ -69,6 +69,7 @@  int timer_init(void)

 	return 0;
 }
+INIT_FUNC(r, 050, timer_init);

 /* Allow boards to override udelay implementation */
 void __udelay(unsigned long usec)
diff --git a/arch/x86/cpu/u-boot.lds b/arch/x86/cpu/u-boot.lds
index fe28030..7b2ab7a 100644
--- a/arch/x86/cpu/u-boot.lds
+++ b/arch/x86/cpu/u-boot.lds
@@ -54,6 +54,16 @@  SECTIONS
 	.got : { *(.got*) }

 	. = ALIGN(4);
+	__initfuncs_r_start = .;
+	.initfuncs_r : { KEEP(*(SORT_BY_NAME(.initfuncs_r*))) }
+	__initfuncs_r_end = .;
+
+	. = ALIGN(4);
+	__initfuncs_f_start = .;
+	.initfuncs_f : { KEEP(*(SORT_BY_NAME(.initfuncs_f*))) }
+	__initfuncs_f_end = .;
+
+	. = ALIGN(4);
 	__data_end = .;

 	. = ALIGN(4);
diff --git a/arch/x86/lib/board.c b/arch/x86/lib/board.c
index b1b8680..e023f58 100644
--- a/arch/x86/lib/board.c
+++ b/arch/x86/lib/board.c
@@ -64,6 +64,10 @@  extern ulong __rel_dyn_start;
 extern ulong __rel_dyn_end;
 extern ulong __bss_start;
 extern ulong __bss_end;
+extern ulong __initfuncs_f_start;
+extern ulong __initfuncs_f_end;
+extern ulong __initfuncs_r_start;
+extern ulong __initfuncs_r_end;

 /************************************************************************
  * Init Utilities							*
@@ -83,6 +87,7 @@  static int init_baudrate (void)

 	return (0);
 }
+INIT_FUNC(f, 040, init_baudrate);

 static int display_banner (void)
 {
@@ -101,6 +106,7 @@  static int display_banner (void)

 	return (0);
 }
+INIT_FUNC(r, 060, display_banner);

 static int display_dram_config (void)
 {
@@ -115,6 +121,7 @@  static int display_dram_config (void)

 	return (0);
 }
+INIT_FUNC(r, 070, display_dram_config);

 static void display_flash_config (ulong size)
 {
@@ -152,34 +159,6 @@  static int copy_uboot_to_ram(void);
 static int clear_bss(void);
 static int do_elf_reloc_fixups(void);

-init_fnc_t *init_sequence_f[] = {
-	cpu_init_f,
-	board_early_init_f,
-	env_init,
-	init_baudrate,
-	serial_init,
-	console_init_f,
-	dram_init_f,
-	calculate_relocation_address,
-	copy_uboot_to_ram,
-	clear_bss,
-	do_elf_reloc_fixups,
-
-	NULL,
-};
-
-init_fnc_t *init_sequence_r[] = {
-	cpu_init_r,		/* basic cpu dependent setup */
-	board_early_init_r,	/* basic board dependent setup */
-	dram_init,		/* configure available RAM banks */
-	interrupt_init,		/* set up exceptions */
-	timer_init,
-	display_banner,
-	display_dram_config,
-
-	NULL,
-};
-
 gd_t *gd;

 static int calculate_relocation_address(void)
@@ -201,6 +180,7 @@  static int calculate_relocation_address(void)

 	return 0;
 }
+INIT_FUNC(f, 080, calculate_relocation_address);

 static int copy_uboot_to_ram(void)
 {
@@ -213,6 +193,7 @@  static int copy_uboot_to_ram(void)

 	return 0;
 }
+INIT_FUNC(f, 090, copy_uboot_to_ram);

 static int clear_bss(void)
 {
@@ -227,6 +208,7 @@  static int clear_bss(void)

 	return 0;
 }
+INIT_FUNC(f, 100, clear_bss);

 static int do_elf_reloc_fixups(void)
 {
@@ -241,18 +223,25 @@  static int do_elf_reloc_fixups(void)

 	return 0;
 }
+INIT_FUNC(f, 110, do_elf_reloc_fixups);
+
+static void do_init_loop(init_fnc_t **fnc, init_fnc_t **end)
+{
+	do {
+		if ((*fnc)() != 0)
+	 			hang();
+	} while (fnc++ < end);
+}

 /* Load U-Boot into RAM, initialize BSS, perform relocation adjustments */
 void board_init_f(ulong boot_flags)
 {
-	init_fnc_t **init_fnc_ptr;
+	init_fnc_t **init_fnc_start = (init_fnc_t **)(&__initfuncs_f_start);
+	init_fnc_t **init_fnc_end = (init_fnc_t **)(&__initfuncs_f_end);

 	gd->flags = boot_flags;

-	for (init_fnc_ptr = init_sequence_f; *init_fnc_ptr; ++init_fnc_ptr) {
-		if ((*init_fnc_ptr)() != 0)
-			hang();
-	}
+	do_init_loop(init_fnc_start, --init_fnc_end);

 	gd->flags |= GD_FLG_RELOC;

@@ -269,7 +258,9 @@  void board_init_r(gd_t *id, ulong dest_addr)
 	ulong size;
 	static bd_t bd_data;
 	static gd_t gd_data;
-	init_fnc_t **init_fnc_ptr;
+
+	init_fnc_t **init_fnc_start = (init_fnc_t **)(&__initfuncs_r_start);
+	init_fnc_t **init_fnc_end = (init_fnc_t **)(&__initfuncs_r_end);

 	show_boot_progress(0x21);

@@ -289,12 +280,11 @@  void board_init_r(gd_t *id, ulong dest_addr)
 	mem_malloc_init((((ulong)dest_addr - CONFIG_SYS_MALLOC_LEN)+3)&~3,
 			CONFIG_SYS_MALLOC_LEN);

-	for (init_fnc_ptr = init_sequence_r; *init_fnc_ptr; ++init_fnc_ptr) {
-		if ((*init_fnc_ptr)() != 0)
-			hang ();
-	}
+	do_init_loop(init_fnc_start, --init_fnc_end);
 	show_boot_progress(0x23);

+
+
 #ifdef CONFIG_SERIAL_MULTI
 	serial_initialize();
 #endif
diff --git a/arch/x86/lib/pcat_interrupts.c b/arch/x86/lib/pcat_interrupts.c
index 2caae20..46514b0 100644
--- a/arch/x86/lib/pcat_interrupts.c
+++ b/arch/x86/lib/pcat_interrupts.c
@@ -82,6 +82,7 @@  int interrupt_init(void)

 	return 0;
 }
+INIT_FUNC(r, 040, interrupt_init);

 void mask_irq(int irq)
 {
diff --git a/board/eNET/eNET.c b/board/eNET/eNET.c
index 2a5636c..f49c3a5 100644
--- a/board/eNET/eNET.c
+++ b/board/eNET/eNET.c
@@ -106,6 +106,7 @@  int board_early_init_f(void)

 	return 0;
 }
+INIT_FUNC(f, 020, board_early_init_f);

 static void enet_setup_pars(void)
 {
@@ -161,6 +162,7 @@  int board_early_init_r(void)

 	return 0;
 }
+INIT_FUNC(r, 020, board_early_init_r);

 void show_boot_progress(int val)
 {
diff --git a/common/console.c b/common/console.c
index 8c650e0..559c799 100644
--- a/common/console.c
+++ b/common/console.c
@@ -531,6 +531,7 @@  int console_init_f(void)

 	return 0;
 }
+INIT_FUNC(f, 060, console_init_f);

 void stdio_print_current_devices(void)
 {
diff --git a/common/env_flash.c b/common/env_flash.c
index 50ca4ffa..a63c108 100644
--- a/common/env_flash.c
+++ b/common/env_flash.c
@@ -126,6 +126,7 @@  int  env_init(void)

 	return 0;
 }
+INIT_FUNC(f, 030, env_init);

 #ifdef CMD_SAVEENV
 int saveenv(void)
@@ -252,6 +253,7 @@  int  env_init(void)
 	gd->env_valid = 0;
 	return 0;
 }
+INIT_FUNC(f, 030, env_init);

 #ifdef CMD_SAVEENV

diff --git a/common/serial.c b/common/serial.c
index 995d268..5d097d8 100644
--- a/common/serial.c
+++ b/common/serial.c
@@ -168,6 +168,7 @@  int serial_init (void)

 	return serial_current->init ();
 }
+INIT_FUNC(f, 050, serial_init);

 void serial_setbrg (void)
 {
diff --git a/include/common.h b/include/common.h
index 12a1074..61126f1 100644
--- a/include/common.h
+++ b/include/common.h
@@ -39,8 +39,16 @@  typedef volatile unsigned char	vu_char;
 #include <linux/bitops.h>
 #include <linux/types.h>
 #include <linux/string.h>
+#include <linux/compiler.h>
 #include <asm/ptrace.h>
 #include <stdarg.h>
+
+typedef int (*initfunc_t)(void);
+
+#define INIT_FUNC(stage,step,fn) \
+	static initfunc_t __initfunc_ ## fn ## stage __used \
+	__attribute__((__section__(".initfuncs_" #stage "." #step))) = fn
+
 #if defined(CONFIG_PCI) && (defined(CONFIG_4xx) && !defined(CONFIG_AP1000))
 #include <pci.h>
 #endif