天天看點

【linux 系統移植④】uboot 進入 main_loop() 前

文章目錄

  • ​​uboot 進入 main_loop() 前​​
  • ​​. arch/arm/cpu/u-boot.lds​​
  • ​​. arch/arm/cpu/armv7/start.S​​
  • ​​.設定異常向量表。​​
  • ​​. 定義uboot中斷處理函數棧​​
  • ​​. reset 段:進入 SVC 模式、關閉 IRQ、FIQ.....​​
  • ​​. cp15協處理器的工作:修改異常向量表的入口位址為\_start、調用 lowlevel_init 函數.....​​
  • ​​.arch/arm/lib/ctr0.S/\_main​​
  • ​​初始化 board_init_f() 環境​​
  • ​​.調用 board_init_f()​​
  • ​​.serial_init​​
  • ​​.dram_init​​
  • ​​relocate_code​​
  • ​​board_init_r​​

uboot 進入 main_loop() 前

主要檔案:start.S,_main(board_init_f,relocate_code,board_init_r)

. arch/arm/cpu/u-boot.lds

連結腳本預設目錄。

makefile、relocate_code、uboot.lds.md

. arch/arm/cpu/armv7/start.S

1. 初始化異常向量表
2. 設定 SVC 模式
3. 關中斷
4. 配置cp15協處理器
5. 初始化 mmu、cache、tlb(cpu_init_cp15)
6. 闆級初始化(cpu_init_crit)      

.設定異常向量表。

#include <asm-offsets.h>
#include <config.h>
#include <version.h>
#include <asm/system.h>
#include <linux/linkage.h>

.globl _start // 聲明 _start 為全局符号,_start 會被連結器連結,連結腳本的入口位址
    /*
    設定異常向量表(4B/item):
    0. 複位異常:複位電平有效時,程式跳轉到複位處理程式處執行
    1. 未定義指令異常:遇到不能處理的指令時,産生未定義指令異常
    2. 軟體中斷異常:執行SWI指令産生的異常
    3. 預存指令異常:處理器預取指令的位址不存在,或該位址不允許目前指令通路,産生指令預取終止異常
    4. 資料操作異常:處理器資料通路指令的位址不存在時,或該位址不允許目前指令通路時,産生資料中止異常
    5. 未使用
    6. IRQ:外部中斷請求有效,且 CPSR 中的 I 位為 0 時,産生 IRQ 異常
    7. FIQ:快速中斷請求引腳有效,且 CPSR 中的 F 位為 0 時,産生 FIQ 異常
    */
_start: b   reset
    ldr pc, _undefined_instruction // 跳轉指令
    ldr pc, _software_interrupt
    ldr pc, _prefetch_abort
    ldr pc, _data_abort
    ldr pc, _not_used
    ldr pc, _irq
    ldr pc, _fiq
#ifdef CONFIG_SPL_BUILD
_undefined_instruction: .word _undefined_instruction
_software_interrupt:    .word _software_interrupt
_prefetch_abort:    .word _prefetch_abort
_data_abort:        .word _data_abort
_not_used:      .word _not_used
_irq:           .word _irq
_fiq:           .word _fiq
_pad:           .word 0x12345678 /* now 16*4=64 */
#else // 如果沒有定義 CONFIG_SPL_BUILD...
_undefined_instruction: .word undefined_instruction
_software_interrupt:    .word software_interrupt
_prefetch_abort:    .word prefetch_abort
_data_abort:        .word data_abort
_not_used:      .word not_used
_irq:           .word irq
_fiq:           .word fiq
_pad:           .word 0x12345678 /* now 16*4=64 */
#endif  /* CONFIG_SPL_BUILD */

.global _end_vect
_end_vect:

    .balignl 16,0xdeadbeef // 指定接下來的代碼要16位元組對齊,空缺的用0xdeadbeef(非法值),友善更加高效的通路記憶體      

. 定義uboot中斷處理函數棧

#ifdef CONFIG_USE_IRQ // 如果uboot使用中斷,這裡會聲明中斷處理函數棧起始位址
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
  .word 0x0badc0de // 中斷處理函數棧起始位址

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
  .word 0x0badc0de
#endif

/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
  .word 0x0badc0de      

. reset 段:進入 SVC 模式、關閉 IRQ、FIQ…

// cpu 上電或者重新開機後執行的代碼
reset:
  bl  save_boot_params // 再跳轉到save_boot_params, 實際什麼也沒做,直接傳回,畢竟棧沒有初始化
  /*
   * 修改 CPSR 寄存器(程式狀态寄存器),設定處理器進入 SVC 模式,并且關掉 IRQ、FIQ
   */
  mrs r0, cpsr
  and r1, r0, #0x1f   @ mask mode bits
  teq r1, #0x1a   @ test for HYP mode
  bicne r0, r0, #0x1f   @ clear all mode bits
  orrne r0, r0, #0x13   @ set SVC mode
  orr r0, r0, #0xc0   @ disable FIQ and IRQ
  msr cpsr,r0
...
ENTRY(save_boot_params)
  bx  lr      @ back to my caller // 什麼也沒做,傳回,棧沒有初始化,最好不要在這裡有操作
ENDPROC(save_boot_params)
  .weak save_boot_params // 如果該函數在其它地方沒有定義,則為空函數,有則定義該函數      

. cp15協處理器的工作:修改異常向量表的入口位址為_start、調用 lowlevel_init 函數…

cp15 協處理器的工作:

1. 設定異常向量入口
2. cpu_init_cp15:配置 cp15 協處理器相關寄存器來設定處理器的 MMU、cache 以及 TLB。
3. cpu_init_crit:調用 `lowlevel_init函數` —— ddr、clk 初始化      
/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
/*
對cp15協處理器進行操作
 */
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
  /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
  mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register
  bic r0, #CR_V   @ V = 0
  mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register

  /* Set vector address in CP15 VBAR register */
  ldr r0, =_start // 修改了ARM預設的異常向量表入口位址,将0x00變為了_start
  mcr p15, 0, r0, c12, c0, 0  @Set VBAR
#endif

  /* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT // 如果沒有定義這個宏...
  bl  cpu_init_cp15
  bl  cpu_init_crit
#endif

  bl  _main // 跳轉到 main start.S 使命結束
...      

.arch/arm/lib/ctr0.S/_main

初始化 board_init_f() 環境

/*這個檔案處理uboot啟動中與生成目标無關的階段,即準備C環境。
該檔案由 _start.S 進入。
主要執行順序是:
1. 為調用 board_init_f() 設定初始化環境
    該環境提供了一個棧和存儲 GD 資料結構的地方
2. 調用 board_init_f() —— 為硬體做準備
    使用 GD 存儲資料 —— relocation destination、future stack、future GD location
(非SPL建構)
3. 建立中間環境
    堆棧和GD為board_init_f()配置設定的,接下來設定BSS和SS
4. 調用relocate_code()
    将uboot從目前位置前往board_init_f()中得到的目的位址
5. 為調用board_init_r()設定環境
    BSS:初始化為0
    初始化 non-const 資料
    在系統中設定堆棧
    一些CPU需要調用c_runtime_cpu_setup() 為GD做一些工作
6. 前往board_init_r()*/

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
    ldr sp, =(CONFIG_SPL_STACK)
#else
    ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) // 加載棧指針到sp中
#endif
    bic sp, sp, #7  /* 8-byte alignment for ABI compliance */ // 8 位元組對齊
    sub sp, #GD_SIZE    /* allocate one GD above SP */ // 減去GD_SIZE的大小
    bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
    mov r9, sp      /* GD is above SP */ // r9寄存器儲存gd結構體的首位址
    mov r0, #0
    bl  board_init_f

...      
#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r9") // 使用 gd 結構體需要加入宏定義      

.調用 board_init_f()

初始化 gd 
調用執行 init_sequence
  有必要關注的初始化函數:
  arch_cpu_init: 可以先寫一個空函數啟動uboot
  timer_init 在lib/time.c中有實作,也是空函數,但是有__WEAK關鍵字,如果自己實作,則會調用自己實作的這個函數
  serial_init
  mark_bootstage
  env_init,   /* initialize environment */
  init_baudrate,    /* initialze baudrate settings */
  serial_init,    /* serial communications setup */
  console_init_f,   /* stage 1 init of console */
  display_banner,   /* say that we are here */
  dram_init      
// board.c
void board_init_f(ulong bootflag)
{
    ...
        
    gd->mon_len = _bss_end_ofs; // 初始化mon_len,代表uboot code的大小
    
    ...
    // 周遊調用 init_sequence 所有函數
    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }

    ...
}

...
//
// 數組内容是指向函數的指針
init_fnc_t *init_sequence[] = {
    arch_cpu_init,      // 可以寫一個空函數先啟動uboot
    mark_bootstage,
#ifdef CONFIG_OF_CONTROL
    fdtdec_check_fdt,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
    board_early_init_f,
#endif
    timer_init,     // timer_init在lib/time.c中有實作,也是空函數,但是有__WEAK關鍵字,如果自己實作,則會調用自己實作的這個函數
#ifdef CONFIG_BOARD_POSTCLK_INIT
    board_postclk_init,
#endif
#ifdef CONFIG_FSL_ESDHC
    get_clocks,
#endif
    env_init,       /* initialize environment */
    init_baudrate,      /* initialze baudrate settings */
    serial_init,        // 精簡uboot啟動必須
    console_init_f,     // 精簡uboot啟動必須
    display_banner,     /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
    print_cpuinfo,      /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
    checkboard,     /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
    init_func_i2c,
#endif
    dram_init,      // 精簡uboot啟動必須
    NULL, // 退出循環
};      
回到 board_init_f,剩餘代碼将會對sdram空間進行規劃      
// board_init_f

// 一部分記憶體空間隐藏
...
#if defined(CONFIG_SYS_MEM_TOP_HIDE) // CONFIG_SYS_MEM_TOP_HIDE 将一部分記憶體空間隐藏
  gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif

  addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size; // 可用sdram的頂端
...

// 預留出tlb空間
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
  /* reserve TLB table */
  gd->arch.tlb_size = 4096 * 4; // 如果打開了 icache 以及 dcache,則預留出PATABLE_SIZE大小的tlb空間
  addr -= gd->arch.tlb_size; 

  /* round down to next 64 kB limit */
  addr &= ~(0x10000 - 1);

  gd->arch.tlb_addr = addr; // tlb空間存放首位址
  debug("TLB table from %08lx to %08lx\n", addr, addr + gd->arch.tlb_size);
#endif

  /* round down to next 4 kB limit */
  addr &= ~(4096 - 1);
  debug("Top of RAM usable for U-Boot at: %08lx\n", addr);

// 确定 frambuffer
#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
  gd->fb_base = CONFIG_FB_ADDR; // 擷取frambuffer大小
#else
  /* reserve memory for LCD display (always full pages) */
  addr = lcd_setmem(addr);
  gd->fb_base = addr; // framebuffer 首位址
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */

  /*
   * reserve memory for U-Boot code, data & bss
   * round down to next 4 kB limit
   */
  addr -= gd->mon_len; // 為uboot的code留出空間
  addr &= ~(4096 - 1);

  debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);

// 确定 addr_sp 位置
#ifndef CONFIG_SPL_BUILD
  /*
   * 預留 malloc len 空間
   */
  addr_sp = addr - TOTAL_MALLOC_LEN;
  debug("Reserving %dk for malloc() at: %08lx\n",
      TOTAL_MALLOC_LEN >> 10, addr_sp);
  /*
   * (permanently) allocate a Board Info struct
   * and a permanent copy of the "global" data
   */
  addr_sp -= sizeof (bd_t);
  bd = (bd_t *) addr_sp;
  gd->bd = bd; // 全局資訊 bd_t 結構體空間的首位址存在 gd->bd
  debug("Reserving %zu Bytes for Board Info at: %08lx\n",
      sizeof (bd_t), addr_sp);

#ifdef CONFIG_MACH_TYPE
  gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif

  addr_sp -= sizeof (gd_t);
  id = (gd_t *) addr_sp; // gd_t 結構體的空間首位址在gd->bd
  debug("Reserving %zu Bytes for Global Data at: %08lx\n",
      sizeof (gd_t), addr_sp);

#if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL)
  /*
   * If the device tree is sitting immediate above our image then we
   * must relocate it. If it is embedded in the data section, then it
   * will be relocated with other data.
   */
  if (gd->fdt_blob) {
    fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
    addr_sp -= fdt_size;
    new_fdt = (void *)addr_sp;
    debug("Reserving %zu Bytes for FDT at: %08lx\n",
          fdt_size, addr_sp);
  }
#endif

  /* setup stackpointer for exeptions */
  gd->irq_sp = addr_sp; // 異常棧指針
#ifdef CONFIG_USE_IRQ
  addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
  debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
    CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
  // 留出12位元組
  addr_sp -= 12;

  /* 8-byte alignment for ABI compliance */
  addr_sp &= ~0x07;
#else
  addr_sp += 128; /* leave 32 words for abort-stack   */
  gd->irq_sp = addr_sp;
#endif
  interrupt_init();

  debug("New Stack Pointer is: %08lx\n", addr_sp);

#ifdef CONFIG_POST
  post_bootmode_init();
  post_run(NULL, POST_ROM | post_bootmode_get(0));
#endif

  gd->bd->bi_baudrate = gd->baudrate;
  /* Ram ist board specific, so move it to board code ... */
  dram_init_banksize();
  display_dram_config();  /* and display it */

  gd->relocaddr = addr;
  gd->start_addr_sp = addr_sp;
  gd->reloc_off = addr - _TEXT_BASE;
  debug("relocation Offset is: %08lx\n", gd->reloc_off);
  if (new_fdt) {
    memcpy(new_fdt, gd->fdt_blob, fdt_size);
    gd->fdt_blob = new_fdt;
  }
  memcpy(id, (void *)gd, sizeof(gd_t));
}      

.serial_init

gd 中的 have_console 字段就是該函數設定的      
// serial.c
int serial_init(void)
{
  return get_current()->start(); // 序列槽驅動中給出的預設調試序列槽結構體,執行start,做一些特定序列槽初始化:console_init_f 将gd中have_console置1
}
static struct serial_device *get_current(void)
{
  struct serial_device *dev;

  if (!(gd->flags & GD_FLG_RELOC))
    dev = default_serial_console();
  else if (!serial_current) // serial_current 用來存放我們目前要使用的 serial
    dev = default_serial_console(); // default_serial_console 在 srial 驅動中有實作,來傳回一個預設的調試序列槽
  else
    dev = serial_current;

  /* We must have a console device */
  if (!dev) {
#ifdef CONFIG_SPL_BUILD
    puts("Cannot find console\n");
    hang();
#else
    panic("Cannot find console\n");
#endif
  }

  return dev;
}      

.dram_init

對gd的ram_size字段進行設定(dram_init實作可以通過配置檔案定義宏定義來實作,也可以通過對ddrc控制器讀擷取dram資訊。)      
// misc.c
int dram_init(void)
{
  gd->ram_size = get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE);
  return 0;
}
// memsize.c
long get_ram_size(long *base, long maxsize)
{
  volatile long *addr;
  long           save[32];
  long           cnt;
  long           val;
  long           size;
  int            i = 0;

  for (cnt = (maxsize / sizeof (long)) >> 1; cnt > 0; cnt >>= 1) {
    addr = base + cnt;  /* pointer arith! */
    sync ();
    save[i++] = *addr;
    sync ();
    *addr = ~cnt;
  }

  addr = base;
  sync ();
  save[i] = *addr;
  sync ();
  *addr = 0;

  sync ();
  if ((val = *addr) != 0) {
    /* Restore the original data before leaving the function.
     */
    sync ();
    *addr = save[i];
    for (cnt = 1; cnt < maxsize / sizeof(long); cnt <<= 1) {
      addr  = base + cnt;
      sync ();
      *addr = save[--i];
    }
    return (0);
  }

  for (cnt = 1; cnt < maxsize / sizeof (long); cnt <<= 1) {
    addr = base + cnt;  /* pointer arith! */
    val = *addr;
    *addr = save[--i];
    if (val != ~cnt) {
      size = cnt * sizeof (long);
      /* Restore the original data before leaving the function.
       */
      for (cnt <<= 1; cnt < maxsize / sizeof (long); cnt <<= 1) {
        addr  = base + cnt;
        *addr = save[--i];
      }
      return (size);
    }
  }

  return (maxsize);
}      

relocate_code

board_init_r

gd->flags |= GD_FLG_RELOC; // 标志已經relocate
使能cache
調用闆級支援函數—— board_init()
serial_initialize();
對malloc預留的空間初始化,起始位址,結束位址,清空。
接下來的代碼是做一些外設的初始化
  比如 mmc flash eth,環境變量的設定,還有中斷的使能
  ...
進入死循環
for (;;) {
  main_loop();
}      
// cr0.s
...
here:
/* Set up final (full) environment */

    bl  c_runtime_cpu_setup /* we still call old routine here */
    // bss 段清空
    ldr r0, =__bss_start    /* this is auto-relocated! */
    ldr r1, =__bss_end      /* this is auto-relocated! */

    mov r2, #0x00000000     /* prepare zero to clear BSS */

clbss_l:cmp r0, r1          /* while not at end of BSS */
    strlo   r2, [r0]        /* clear 32-bit BSS word */
    addlo   r0, r0, #4      /* move to next */
    blo clbss_l

    bl coloured_LED_init    // 實作上電後訓示燈亮
    bl red_led_on

    /* call board_init_r(gd_t *id, ulong dest_addr) */
    mov     r0, r9                  /* gd_t */
    ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
    /* call board_init_r */
    ldr pc, =board_init_r   /* this is auto-relocated! */

    /* we should not return here. */

#endif

ENDPROC(_main)

// ...
ENTRY(c_runtime_cpu_setup)
/*
 * 如果icache是enable,則無效掉icache,保證從sdram中更新指令到cache中
 */
#ifndef CONFIG_SYS_ICACHE_OFF
    mcr p15, 0, r0, c7, c5, 0   @ invalidate icache
    mcr     p15, 0, r0, c7, c10, 4  @ DSB 
    mcr     p15, 0, r0, c7, c5, 4   @ ISB 
#endif
/*
 * 更新異常向量表首位址
 */
    /* Set vector address in CP15 VBAR register */
    ldr     r0, =_start
    mcr     p15, 0, r0, c12, c0, 0  @Set VBAR
 
    bx  lr  
 
ENDPROC(c_runtime_cpu_setup)      
// board.c
void board_init_r(gd_t *id, ulong dest_addr)
{
  ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
  ulong flash_size;
#endif

  gd->flags |= GD_FLG_RELOC;  // 置位 gd->flags,标志已經relocate
  bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r"); // 調試用

  monitor_flash_len = _end_ofs;

  /* Enable caches */
  enable_caches();

  debug("monitor flash len: %08lX\n", monitor_flash_len);
  board_init(); /* Setup chipselects */
  /*
   * TODO: printing of the clock inforamtion of the board is now
   * implemented as part of bdinfo command. Currently only support for
   * davinci SOC's is added. Remove this check once all the board
   * implement this.
   */
#ifdef CONFIG_CLOCKS
  set_cpu_clk_info(); /* Setup clock information */
#endif
  serial_initialize(); // drivers/serial/serial.c 所有序列槽驅動都會實作一個xxxx_serial_initialize函數,并且添加到serial_initialize中,總結一下,serial_initialize工作是将所有serial驅動中所有序列槽注冊到serial_devices連結清單中,然後找到指定的預設序列槽。

  debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);

#ifdef CONFIG_LOGBUFFER
  logbuff_init_ptrs();
#endif
#ifdef CONFIG_POST
  post_output_backlog();
#endif

  /* The Malloc area is immediately below the monitor copy in DRAM */
  malloc_start = dest_addr - TOTAL_MALLOC_LEN;
  mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN); // 對malloc預留的空間初始化,起始位址,結束位址,清空。

#ifdef CONFIG_ARCH_EARLY_INIT_R
  arch_early_init_r();
#endif
  power_init_board();

#if !defined(CONFIG_SYS_NO_FLASH)
  puts("Flash: ");

  flash_size = flash_init();
  if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
    print_size(flash_size, "");
    /*
     * Compute and print flash CRC if flashchecksum is set to 'y'
     *
     * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
     */
    if (getenv_yesno("flashchecksum") == 1) {
      printf("  CRC: %08X", crc32(0,
        (const unsigned char *) CONFIG_SYS_FLASH_BASE,
        flash_size));
    }
    putc('\n');
# else  /* !CONFIG_SYS_FLASH_CHECKSUM */
    print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
  } else {
    puts(failed);
    hang();
  }
#endif

#if defined(CONFIG_CMD_NAND)
  puts("NAND:  ");
  nand_init();    /* go init the NAND */
#endif

#if defined(CONFIG_CMD_ONENAND)
  onenand_init();
#endif

#ifdef CONFIG_GENERIC_MMC
  puts("MMC:   ");
  mmc_initialize(gd->bd);
#endif

#ifdef CONFIG_HAS_DATAFLASH
  AT91F_DataflashInit();
  dataflash_print_info();
#endif

  /* initialize environment */
  if (should_load_env())
    env_relocate();
  else
    set_default_env(NULL);

#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
  arm_pci_init();
#endif

  stdio_init(); /* get the devices list going. */

  jumptable_init();

#if defined(CONFIG_API)
  /* Initialize API */
  api_init();
#endif

  console_init_r(); /* fully init console as a device */

#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
# ifdef CONFIG_OF_CONTROL
  /* Put this here so it appears on the LCD, now it is ready */
  display_fdt_model(gd->fdt_blob);
# else
  checkboard();
# endif
#endif

#if defined(CONFIG_ARCH_MISC_INIT)
  /* miscellaneous arch dependent initialisations */
  arch_misc_init();
#endif
#if defined(CONFIG_MISC_INIT_R)
  /* miscellaneous platform dependent initialisations */
  misc_init_r();
#endif

  /* enable exceptions */
  enable_interrupts();

  /* Initialize from environment */
  load_addr = getenv_ulong("loadaddr", 16, load_addr);

#ifdef CONFIG_BOARD_LATE_INIT
  board_late_init();
#endif

#ifdef CONFIG_BITBANGMII
  bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
  puts("Net:   ");
  eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
  debug("Reset Ethernet PHY\n");
  reset_phy();
#endif
#endif

#ifdef CONFIG_POST
  post_run(NULL, POST_RAM | post_bootmode_get(0));
#endif

#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
  /*
   * Export available size of memory for Linux,
   * taking into account the protected RAM at top of memory
   */
  {
    ulong pram = 0;
    uchar memsz[32];

#ifdef CONFIG_PRAM
    pram = getenv_ulong("pram", 10, CONFIG_PRAM);
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
    /* Also take the logbuffer into account (pram is in kB) */
    pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024;
#endif
#endif
    sprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram);
    setenv("mem", (char *)memsz);
  }
#endif

  /* main_loop() can return to retry autoboot, if so just run it again. */
  for (;;) {
    main_loop();
  }

  /* NOTREACHED - no way out of command loop except booting */
}