AM335X開發—— uboot2016.5 SPL階段分析

一般的芯片啓動流程都是Romcode –> BL1 –> BL2 –> Kernel –> User Image,

TI的芯片也不例外,Romcode –> ML0(SPL) –> uboot.img

整個boot階段被分爲三部分,

第一部分是芯片固化的Romcode,上電自動執行,一般支持flash,sd,uart,usb等啓動方式,引導加載spl至片內ram運行;

第二部分是uboot spl,這裏被稱爲ML0,是uboot的第一階段,主要是初始化必要的硬件外設,關閉看門狗,關中斷,配置時鐘,初始化外部RAM,Flash控制器等。然後從對應啓動方式中獲取uboot.img,也就是uboot的第二階段,加載到片外sdram中運行;

第三部分是uboot.img,主要是板級初始化,用來引導加載內核。

 

下面針對Romcode –> ML0(SPL) –> uboot.img這個流程做一個說明:

1.Romcode

芯片的Boot Rom,存放在總計176KB的ROM當中,如下圖:

 

該地址區域存放了芯片的引導加載程序,板子上電後會自動執行這些代碼。 

2. ML0(SPL)

系統上電後根據啓動方式,到對應的地方把MLO加載到芯片內部的RAM中運行。

 

圖中的Downloaded Image區域是用來保存 MLO(SPL)文件的,其最大可達到 109 KB

Downloaded Image = 0X4030B800 – 0X402F0400 = 0X1B400 = 109KB

MLO(SPL)的內存分佈:

3.uboot.img

該階段的代碼搬到了芯片外的SDRAM中運行:(物理起始地址爲0x8000000)

 uboot.img的內存分佈:

 

在對以上概念有一個整體的瞭解之後,下面開始來分析uboot的SPL階段:

 

一、SPL的簡介:

SPL(Secondary programloader)是uboot第一階段執行的代碼。主要負責搬移uboot第二階段的代碼到片外內存中運行。SPL是由固化在芯片內部的ROM引導的。我們知道很多芯片廠商固化的ROM支持從nandflash、SD卡等外部介質啓動。所謂啓動,就是從這些外部介質中搬移一段固定大小(4K/8K/16K等)的代碼到內部RAM中運行。這裏搬移的就是SPL。在最新版本的uboot中,可以看到SPL也支持nandflash,SD卡等多種啓動方式。當SPL本身被搬移到內部RAM中運行時,它會從nandflash、SD卡等外部介質中搬移uboot第二階段的代碼到片外內存中。

 

二、SPL的功能:

1.Basic Arm Initialization

2.UART console initialization

3.Clocks and DPLL Locking(minimal)

4.SDRAM initialization

5.Mux(minimal)

6.Boot Device Initialization, based on where we are booting from MMC1, or MMC2,or Nand, or Onenand

7.Bootloading real u-boot from the Boot Device and passing control to it.

三、SPL鏈接文件U-boot-spl.lds

鏈接文件決定一個可執行程序的各個段的存儲(加載)地址,以及運行(鏈接)地址。下面來看看SPL的鏈接文件U-boot-spl.lds:

 

從該鏈接文件中可以看出,程序執行是從_start標號開始的。

_start位於vectors.S (arch/arm/lib)

  1. _start:
  2. #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
  3. .word CONFIG_SYS_DV_NOR_BOOT_CFG
  4. #endif
  5. /*
  6. 跳轉到reset之後就不會返回了。不然就是沒關中斷出錯了,
  7. 然後跳轉到下面7種狀態之一:死循環。
  8. */
  9. b reset
  10. ldr pc, _undefined_instruction
  11. ldr pc, _software_interrupt
  12. ldr pc, _prefetch_abort
  13. ldr pc, _data_abort
  14. ldr pc, _not_used
  15. ldr pc, _irq
  16. ldr pc, _fiq
  17. /*
  18. *************************************************************************
  19. *
  20. * Indirect vectors table
  21. *
  22. * Symbols referenced here must be defined somewhere else
  23. *
  24. *************************************************************************
  25. */
  26. .globl _undefined_instruction
  27. .globl _software_interrupt
  28. .globl _prefetch_abort
  29. .globl _data_abort
  30. .globl _not_used
  31. .globl _irq
  32. .globl _fiq
  33. _undefined_instruction: .word undefined_instruction
  34. _software_interrupt: .word software_interrupt
  35. _prefetch_abort: .word prefetch_abort
  36. _data_abort: .word data_abort
  37. _not_used: .word not_used
  38. _irq: .word irq
  39. _fiq: .word fiq
  40. /*
  41. 前面佔據了(8 + 7) * 4 = 60字節 ,不是16的倍數,所以要
  42. 填充0xdeadbeef字段,變成64字節。
  43. */
  44. .balignl 16,0xdeadbeef
  45. /*
  46. *************************************************************************
  47. *
  48. * Interrupt handling
  49. *
  50. *************************************************************************
  51. */
  52. /* SPL interrupt handling: just hang */
  53. #ifdef CONFIG_SPL_BUILD
  54. .align 5
  55. undefined_instruction:
  56. software_interrupt:
  57. prefetch_abort:
  58. data_abort:
  59. not_used:
  60. irq:
  61. fiq:
  62. 1:
  63. bl 1b /* hang and never return */

 reset: 位於start.S (arch/arm/cpu/armv7)

  1. .globl reset
  2. .globl save_boot_params_ret
  3. reset:
  4. /* Allow the board to save important registers */
  5. b save_boot_params
  6. save_boot_params_ret:
  7. /*
  8. * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
  9. * except if in HYP mode already
  10. */
  11. mrs r0, cpsr
  12. and r1, r0, #0x1f @ mask mode bits
  13. teq r1, #0x1a @ test for HYP mode
  14. bicne r0, r0, #0x1f @ clear all mode bits
  15. orrne r0, r0, #0x13 @ set SVC mode
  16. orr r0, r0, #0xc0 @ disable FIQ and IRQ
  17. msr cpsr,r0
  18. /*
  19. * Setup vector:
  20. * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
  21. * Continue to use ROM code vector only in OMAP4 spl)
  22. */
  23. #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
  24. /* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
  25. mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
  26. bic r0, #CR_V @ V = 0
  27. mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
  28. /* Set vector address in CP15 VBAR register */
  29. ldr r0, =_start
  30. mcr p15, 0, r0, c12, c0, 0 @Set VBAR
  31. #endif
  32. /* the mask ROM code should have PLL and others stable */
  33. #ifndef CONFIG_SKIP_LOWLEVEL_INIT
  34. bl cpu_init_cp15
  35. bl cpu_init_crit
  36. #endif
  37. bl _main

 

reset部分做了如下動作:

關閉中斷,設置cpu爲SVC32模式(管理模式)。

cpu_init_cp15初始化了協處理器:關閉cathe以及mmu。

cpu_init_crit準備臨時堆棧,初始化鎖相環以及內存。

  1. ENTRY(cpu_init_crit)
  2. /*
  3. * Jump to board specific initialization...
  4. * The Mask ROM will have already initialized
  5. * basic memory. Go here to bump up clock rate and handle
  6. * wake up conditions.
  7. */
  8. b lowlevel_init @ go setup pll,mux,memory
  9. ENDPROC(cpu_init_crit)

跳轉至lowlevel_init :位於lowlevel_init.S (arch/arm/cpu/armv7) 

  1. ENTRY(lowlevel_init)
  2. /*
  3. * Setup a temporary stack. Global data is not available yet.
  4. */
  5. ldr sp, =CONFIG_SYS_INIT_SP_ADDR
  6. bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
  7. #ifdef CONFIG_SPL_DM
  8. mov r9, #0
  9. #else
  10. /*
  11. * Set up global data for boards that still need it. This will be
  12. * removed soon.
  13. */
  14. #ifdef CONFIG_SPL_BUILD
  15. ldr r9, =gdata
  16. #else
  17. sub sp, sp, #GD_SIZE
  18. bic sp, sp, #7
  19. mov r9, sp
  20. #endif
  21. #endif
  22. /*
  23. * Save the old lr(passed in ip) and the current lr to stack
  24. */
  25. push {ip, lr}
  26. /*
  27. * Call the very early init function. This should do only the
  28. * absolute bare minimum to get started. It should not:
  29. *
  30. * - set up DRAM
  31. * - use global_data
  32. * - clear BSS
  33. * - try to start a console
  34. *
  35. * For boards with SPL this should be empty since SPL can do all of
  36. * this init in the SPL board_init_f() function which is called
  37. * immediately after this.
  38. */
  39. bl s_init
  40. pop {ip, pc}
  41. ENDPROC(lowlevel_init)

爲調用c函數準備一個臨時堆棧,這個堆棧在cpu的片上內存

ldr sp, =CONFIG_SYS_INIT_SP_ADDR

bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

 

跟蹤代碼,找到CONFIG_SYS_INIT_SP_ADDR的定義:

ti_armv7_common.h (include/configs)

#define CONFIG_SYS_INIT_SP_ADDR (NON_SECURE_SRAM_END - GENERATED_GBL_DATA_SIZE)

#define NON_SECURE_SRAM_START   0x402F0400

#define NON_SECURE_SRAM_END     0x40310000

#define SRAM_SCRATCH_SPACE_ADDR 0x4030B800

#define GENERATED_GBL_DATA_SIZE 224 /* (sizeof(struct global_data) + 15) & ~15  @ */

所以算出CONFIG_SYS_INIT_SP_ADDR  = 0x40310000 - 224 = 0x4030FF20

(反彙編u-boot.spl,查看dump文件,發現的確是0x4030FF20)

 繼續跳轉至s_init:  board.c (arch/arm/cpu/armv7/am33xx)

  1. void s_init(void)
  2. {
  3. #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_RTC_ONLY_SUPPORT)
  4. rtc_only();
  5. #endif
  6. /*
  7. * The ROM will only have set up sufficient pinmux to allow for the
  8. * first 4KiB NOR to be read, we must finish doing what we know of
  9. * the NOR mux in this space in order to continue.
  10. */
  11. #ifdef CONFIG_NOR_BOOT
  12. enable_norboot_pin_mux();
  13. #endif
  14. watchdog_disable();
  15. set_uart_mux_conf();
  16. setup_clocks_for_console();
  17. uart_soft_reset();
  18. #if defined(CONFIG_SPL_AM33XX_ENABLE_RTC32K_OSC)
  19. /* Enable RTC32K clock */
  20. rtc32k_enable();
  21. #endif
  22. }

主要工作:關閉看門狗、設置串口的引腳以及時鐘、使能RTC時鐘信號

 

最後返回至reset代碼,跳轉到_main執行:crt0.S (arch/arm/lib)

  1. ENTRY(_main)
  2. /*
  3. * Set up initial C runtime environment and call board_init_f(0).
  4. */
  5. #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
  6. ldr sp, =(CONFIG_SPL_STACK)
  7. #else
  8. ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
  9. #endif
  10. #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
  11. mov r3, sp
  12. bic r3, r3, #7
  13. mov sp, r3
  14. #else
  15. bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
  16. #endif
  17. mov r0, sp //r0 = sp,之後調用board_init_f_alloc_reserve函數,r0作爲形參
  18. bl board_init_f_alloc_reserve
  19. //返回之後,r0裏面存放的是global_data的地址
  20. //注意,同時也是堆棧地址,因爲堆棧是向下增長的,所以不必擔心和global_data衝突的問題
  21. mov sp, r0 //sp = board_init_f_alloc_reserve函數返回的top,也就是global_data的地址
  22. /* set up gd here, outside any C code */
  23. mov r9, r0 //r9 = r0 = global_data的起始地址
  24. //調用board_init_f_init_reserve對global_data進行初始化
  25. bl board_init_f_init_reserve
  26. mov r0, #0
  27. bl board_init_f
  28. #if ! defined(CONFIG_SPL_BUILD)
  29. /*
  30. * Set up intermediate environment (new sp and gd) and call
  31. * relocate_code(addr_moni). Trick here is that we'll return
  32. * 'here' but relocated.
  33. */
  34. ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
  35. #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
  36. mov r3, sp
  37. bic r3, r3, #7
  38. mov sp, r3
  39. #else
  40. bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
  41. #endif
  42. ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
  43. sub r9, r9, #GD_SIZE /* new GD is below bd */
  44. adr lr, here
  45. ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
  46. add lr, lr, r0
  47. #if defined(CONFIG_CPU_V7M)
  48. orr lr, #1 /* As required by Thumb-only */
  49. #endif
  50. ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
  51. b relocate_code
  52. here:
  53. /*
  54. * now relocate vectors
  55. */
  56. bl relocate_vectors
  57. /* Set up final (full) environment */
  58. bl c_runtime_cpu_setup /* we still call old routine here */
  59. #endif
  60. #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
  61. # ifdef CONFIG_SPL_BUILD
  62. /* Use a DRAM stack for the rest of SPL, if requested */
  63. bl spl_relocate_stack_gd
  64. cmp r0, #0
  65. movne sp, r0 //new_gd指針存入sp
  66. movne r9, r0 //new_gd指針存入r9
  67. # endif
  68. ldr r0, =__bss_start /* this is auto-relocated! */
  69. #ifdef CONFIG_USE_ARCH_MEMSET
  70. ldr r3, =__bss_end /* this is auto-relocated! */
  71. mov r1, #0x00000000 /* prepare zero to clear BSS */
  72. subs r2, r3, r0 /* r2 = memset len */
  73. bl memset
  74. #else
  75. ldr r1, =__bss_end /* this is auto-relocated! */
  76. mov r2, #0x00000000 /* prepare zero to clear BSS */
  77. /*
  78. r0 = __bss_start
  79. r1 = __bss_end
  80. r2 = 0
  81. */
  82. clbss_l:cmp r0, r1 /* while not at end of BSS */
  83. #if defined(CONFIG_CPU_V7M)
  84. itt lo
  85. #endif
  86. //HS - Higher or Same, LO - Lower
  87. //__bss_start比__bss_end小,則把r2的值加載到以r0地址的存儲器中
  88. strlo r2, [r0] /* clear 32-bit BSS word */
  89. //r0 = r0 + 4
  90. addlo r0, r0, #4 /* move to next */
  91. blo clbss_l
  92. #endif
  93. #if ! defined(CONFIG_SPL_BUILD)
  94. bl coloured_LED_init
  95. bl red_led_on
  96. #endif
  97. /* call board_init_r(gd_t *id, ulong dest_addr) */
  98. mov r0, r9 /* gd_t */
  99. //r1 = r9 + GD_RELOCADDR
  100. ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
  101. /* call board_init_r */
  102. #if defined(CONFIG_SYS_THUMB_BUILD)
  103. //r0以及r1作爲形參傳遞
  104. ldr lr, =board_init_r /* this is auto-relocated! */
  105. //BX指令跳轉到指令中所指定的目標地址,若目標地址的bit[0]爲0,
  106. //則跳轉時自動將CPSR中的標誌位T復位,即把目標地址的代碼解釋
  107. //爲ARM代碼;若目標地址的bit[0]爲1,則跳轉時自動將CPSR中的標誌位
  108. //T置位,即把目標地址的代碼解釋爲Thumb代碼。
  109. bx lr
  110. #else
  111. ldr pc, =board_init_r /* this is auto-relocated! */
  112. #endif
  113. /* we should not return here. */
  114. #endif
  115. ENDPROC(_main)

ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)

設置棧,上面已經計算出來:

sp = CONFIG_SYS_INIT_SP_ADDR = 0x4030FF20

 

mov r0, sp //r0 = sp,之後調用board_init_f_alloc_reserve函數,r0作爲形參

  1. // 這個函數用於給global_data分配空間,在relocation之前調用
  2. // 傳入的參數是頂部地址,但是不一定是要內存頂部的地址,
  3. //可以自己進行規劃,後面_main函數會說明
  4. ulong board_init_f_alloc_reserve(ulong top)
  5. {
  6. /* Reserve early malloc arena */
  7. #if defined(CONFIG_SYS_MALLOC_F)
  8. // 先從頂部向下分配一塊CONFIG_SYS_MALLOC_F_LEN大小的空間給early malloc使用
  9. // 關於CONFIG_SYS_MALLOC_F_LEN可以參考README
  10. // 這塊內存是用於在relocation前用於給malloc函數提供內存池。
  11. top -= CONFIG_SYS_MALLOC_F_LEN; //top = top - CONFIG_SYS_MALLOC_F_LEN = r0(sp)-CONFIG_SYS_MALLOC_F_LEN
  12. #endif
  13. /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
  14. // 繼續向下分配sizeof(struct global_data)大小的內存給global_data使用,向下16byte對齊
  15. // 這時候得到的地址就是global_data的地址。
  16. top = rounddown(top-sizeof(struct global_data), 16);
  17. // 將top,也就是global_data的地址返回
  18. return top;
  19. }

mov sp, r0 //sp = board_init_f_alloc_reserve函數返回的top,也就是global_data的地址。注意,同時也是堆棧地址,因爲堆棧是向下增長的,所以不必擔心和global_data衝突的問題

mov r9, r0 //r9 = r0 = global_data的起始地址

跳轉至board_init_f_init_reserve函數,r0作爲形參:board_init.c (common/init)

  1. // 這個函數用於對global_data區域進行初始化,也就是清空global_data區域
  2. // 傳入的參數就是global_data的基地址
  3. void board_init_f_init_reserve(ulong base)
  4. {
  5. struct global_data *gd_ptr;
  6. #ifndef _USE_MEMCPY
  7. int *ptr;
  8. #endif
  9. /*
  10. * clear GD entirely and set it up.
  11. * Use gd_ptr, as gd may not be properly set yet.
  12. */
  13. gd_ptr = (struct global_data *)base;
  14. /* zero the area */
  15. #ifdef _USE_MEMCPY
  16. // 先通過memset函數對global_data數據結構進行清零
  17. memset(gd_ptr, '\0', sizeof(*gd));
  18. #else
  19. for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
  20. *ptr++ = 0;
  21. #endif
  22. /* set GD unless architecture did it already */
  23. #if !defined(CONFIG_ARM)
  24. arch_setup_gd(gd_ptr);
  25. #endif
  26. /* next alloc will be higher by one GD plus 16-byte alignment */
  27. // 因爲global_data區域是16Byte對齊的,對齊後,後面的地址就是early malloc
  28. //的內存池的地址,具體參考上述board_init_f_alloc_reserve
  29. // 所以這裏就獲取了early malloc的內存池的地址
  30. base += roundup(sizeof(struct global_data), 16);
  31. /*
  32. * record early malloc arena start.
  33. * Use gd as it is now properly set for all architectures.
  34. */
  35. #if defined(CONFIG_SYS_MALLOC_F)
  36. /* go down one 'early malloc arena' */
  37. // 將內存池的地址寫入到gd->malloc_base中
  38. gd->malloc_base = base;
  39. /* next alloc will be higher by one 'early malloc arena' size */
  40. //加上CONFIG_SYS_MALLOC_F_LEN,獲取early malloc的內存池的末尾地址,
  41. //這裏並沒有什麼作用,是爲了以後在early malloc的內存池後面多
  42. //加一個區域時的修改方便。
  43. base += CONFIG_SYS_MALLOC_F_LEN;
  44. #endif
  45. }

這裏首先爲調用board_init_f準備一個臨時堆棧。然後將堆棧初始的地址保存在r9,所以r9就是gd的起始地址,後面需要靠r9訪問gd中的成員。然後將r0賦值成0,r0就是要調用的board_init_f函數的第一個參數。 

global_data內存分佈如下:

———————- CONFIG_SYS_INIT_SP_ADDR —————————–高地址

…………………………….. early malloc 內存池

————————-early malloc 內存池基地址 —————————

………………………………… global_data區域

—————-global_data基地址(r9), 也是堆棧的起始地址————-

………………………………………堆棧空間

————————————–堆棧結束—————————————-低地址

 

跳轉至board_init_f 函數:board.c (arch/arm/cpu/armv7/am33xx)   

  1. #ifdef CONFIG_SPL_BUILD
  2. void board_init_f(ulong dummy)
  3. {
  4. board_early_init_f();
  5. sdram_init();
  6. }
  7. #endif

該函數主要功能是初始化clock、dpll、timer、IO引腳(mmc、i2c、cpsw、nand)、ddr。

 

然後跳轉至spl_relocate_stack_gd: spl.c (common/spl)

  1. /**
  2. * spl_relocate_stack_gd() - Relocate stack ready for board_init_r() execution
  3. *
  4. * Sometimes board_init_f() runs with a stack in SRAM but we want to use SDRAM
  5. * for the main board_init_r() execution. This is typically because we need
  6. * more stack space for things like the MMC sub-system.
  7. *
  8. * This function calculates the stack position, copies the global_data into
  9. * place, sets the new gd (except for ARM, for which setting GD within a C
  10. * function may not always work) and returns the new stack position. The
  11. * caller is responsible for setting up the sp register and, in the case
  12. * of ARM, setting up gd.
  13. *
  14. * All of this is done using the same layout and alignments as done in
  15. * board_init_f_init_reserve() / board_init_f_alloc_reserve().
  16. *
  17. * @return new stack location, or 0 to use the same stack
  18. */
  19. ulong spl_relocate_stack_gd(void)
  20. {
  21. #ifdef CONFIG_SPL_STACK_R
  22. gd_t *new_gd;
  23. ulong ptr = CONFIG_SPL_STACK_R_ADDR; //0x82000000, SDRAM的地址
  24. #ifdef CONFIG_SPL_SYS_MALLOC_SIMPLE
  25. if (CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN) {
  26. if (!(gd->flags & GD_FLG_SPL_INIT))
  27. panic_str("spl_init must be called before heap reloc");
  28. ptr -= CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN;
  29. gd->malloc_base = ptr;
  30. gd->malloc_limit = CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN;
  31. gd->malloc_ptr = 0;
  32. }
  33. #endif
  34. /* Get stack position: use 8-byte alignment for ABI compliance */
  35. //roundup(x, y)類似於一個數學函數,它總是嘗試找到大於x並接近x的可以整
  36. //除y的那個數,也即向上圓整。
  37. ptr = CONFIG_SPL_STACK_R_ADDR - roundup(sizeof(gd_t),16);
  38. //將之前在片內內存中的global_data對象重定位到SDRAM中(new_gd)
  39. new_gd = (gd_t *)ptr;
  40. memcpy(new_gd, (void *)gd, sizeof(gd_t));
  41. #if !defined(CONFIG_ARM)
  42. gd = new_gd;
  43. #endif
  44. //返回新的棧指針
  45. return ptr;
  46. #else
  47. return 0;
  48. #endif
  49. }

功能在註釋那裏說的很清楚了:

This function calculates the stack position, copies the global_data into place, sets the new gd (except for ARM, for which setting GD within a C function may not always work) and returns the new stack position.

 

返回至_main代碼中,繼續運行(r0存放了新的gd指針,同時也是新的棧指針):

    cmp r0, #0

    movne   sp, r0

    movne   r9, r0

cmp命令是比較r0和0的大小(第一個操作數減去第二個操作數,但不影響第兩個操作數的值,最後結果影響CPSR狀態寄存器):

 

EQ和NE條件碼:當兩個數據相等時,條件碼EQ有效,否則條件碼NE有效。

最後把new_gd指針放在了r9寄存器裏面。

然後繼續往下走,主要功能是清除bss段:(存儲未初始化的全局變量和未初始化的static變量)

  1. ldr r0, =__bss_start /* this is auto-relocated! */
  2. #ifdef CONFIG_USE_ARCH_MEMSET
  3. ldr r3, =__bss_end /* this is auto-relocated! */
  4. mov r1, #0x00000000 /* prepare zero to clear BSS */
  5. subs r2, r3, r0 /* r2 = memset len */
  6. bl memset
  7. #else
  8. ldr r1, =__bss_end /* this is auto-relocated! */
  9. mov r2, #0x00000000 /* prepare zero to clear BSS */
  10. /*
  11. r0 = __bss_start
  12. r1 = __bss_end
  13. r2 = 0
  14. */
  15. clbss_l:cmp r0, r1 /* while not at end of BSS */
  16. #if defined(CONFIG_CPU_V7M)
  17. itt lo
  18. #endif
  19. //HS - Higher or Same, LO - Lower
  20. //__bss_start比__bss_end小,則把r2的值加載到以r0地址的存儲器中
  21. strlo r2, [r0] /* clear 32-bit BSS word */
  22. //r0 = r0 + 4
  23. addlo r0, r0, #4 /* move to next */
  24. blo clbss_l
  25. #endif

然後把新的gd指針存入r0,gd指針加上偏移量GD_RELOCADDR存入r1,兩者作爲形參傳遞給board_init_r,最後跳轉到board_init_r中執行:spl.c (common/spl)

  1. void board_init_r(gd_t *dummy1, ulong dummy2)
  2. {
  3. int i;
  4. debug(">>spl:board_init_r()\n");
  5. //CONFIG_SYS_SPL_MALLOC_START = 0x80a80000, CONFIG_SYS_SPL_MALLOC_SIZE = 0x100000
  6. #if defined(CONFIG_SYS_SPL_MALLOC_START)
  7. //初始化:設置0x80a80000 ~0x80B80000內容爲0
  8. mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
  9. CONFIG_SYS_SPL_MALLOC_SIZE);
  10. gd->flags |= GD_FLG_FULL_MALLOC_INIT;
  11. #endif
  12. if (!(gd->flags & GD_FLG_SPL_INIT)) {
  13. if (spl_init())
  14. hang();
  15. }
  16. #ifndef CONFIG_PPC
  17. /*
  18. * timer_init() does not exist on PPC systems. The timer is initialized
  19. * and enabled (decrementer) in interrupt_init() here.
  20. */
  21. timer_init();
  22. #endif
  23. #ifdef CONFIG_SPL_BOARD_INIT
  24. spl_board_init();
  25. #endif

spl_board_init: boot-common.c (arch/arm/cpu/armv7/omap-common)

主要工作:把片內ram的啓動參數加載到片外內存,初始化串口、gpmc、i2c、usb、看門狗、dpll

其中,裏面的save_omap_boot_params函數就是把片內ram的啓動參數加載到片外內存,獲取boot_device類型以及boot_mode,設置gd結構體。具體的啓動參數結構如下:

  1. void save_omap_boot_params(void)
  2. {
  3. //OMAP_SRAM_SCRATCH_BOOT_PARAMS = 0x4030B800 + 0x24 = 0x4030B824
  4. //boot_params參數存儲在片內的RAM區
  5. u32 boot_params = *((u32 *)OMAP_SRAM_SCRATCH_BOOT_PARAMS);
  6. struct omap_boot_parameters *omap_boot_params;
  7. int sys_boot_device = 0;
  8. u32 boot_device;
  9. u32 boot_mode;
  10. if ((boot_params < NON_SECURE_SRAM_START) ||
  11. (boot_params > NON_SECURE_SRAM_END))
  12. return;
  13. omap_boot_params = (struct omap_boot_parameters *)boot_params;
  14. boot_device = omap_boot_params->boot_device;
  15. boot_mode = MMCSD_MODE_UNDEFINED;

 回到board_init_r函數:

  1. //獲取gd結構體中的boot_device
  2. board_boot_order(spl_boot_list);
  3. for (i = 0; i < ARRAY_SIZE(spl_boot_list) &&
  4. spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
  5. announce_boot_device(spl_boot_list[i]); //打印從哪裏啓動uboot
  6. if (!spl_load_image(spl_boot_list[i])) //找到並且加載u-boot.img到內存
  7. break;
  8. }
  9. if (i == ARRAY_SIZE(spl_boot_list) ||
  10. spl_boot_list[i] == BOOT_DEVICE_NONE) {
  11. puts("SPL: failed to boot from all boot devices\n");
  12. hang();
  13. }
  14. switch (spl_image.os) {
  15. case IH_OS_U_BOOT:
  16. debug("Jumping to U-Boot\n");
  17. break;
  18. #ifdef CONFIG_SPL_OS_BOOT
  19. case IH_OS_LINUX:
  20. debug("Jumping to Linux\n");
  21. spl_board_prepare_for_linux();
  22. jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
  23. #endif
  24. default:
  25. debug("Unsupported OS image.. Jumping nevertheless..\n");
  26. }
  27. #if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SYS_SPL_MALLOC_SIZE)
  28. debug("SPL malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
  29. gd->malloc_ptr / 1024);
  30. #endif
  31. debug("loaded - jumping to U-Boot...");
  32. jump_to_image_no_args(&spl_image); //跳轉到u-boot.img中繼續運行
  33. }

將鏡像加載到內存的時候,此時SPL進行判斷,加載的鏡像是uboot還是kernel,然後便跳轉到鏡像的入口中進行執行。此時,SPL的使命便完成了。

==>jump_to_image_no_args(&spl_image);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章