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);

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
韦东山老师为啥要录升级版嵌入式视频?200x年左右,嵌入式Linux在全世界、在中国刚刚兴起。我记得我2005年进入中兴时,全部门的人正在努力学习Linux。在2008年,我写了一本书《嵌入式Linux应用开发完全手册》。它的大概内容是:裸机、U-boot、Linux内核、Linux设备驱动。那时还没有这样讲解整个系统的书,芯片厂家Linux开发包也还不完善,从bootloader到内核,再到设备驱动都不完善。有全系统开发能力的人也很少。于是这书也就恰逢其时,变成了畅销书。我也根据这个思路录制了视频:裸机、U-boot、Linux内核、Linux设备驱动。收获些许名声,带领很多人进入Linux世界。11年过去了,嵌入式Linux世界发生了翻天覆地的变化① 基本系统能用芯片厂家都会提供完整的U-boot、Linux内核、芯片上硬件资源的驱动。方案厂家会做一些定制,比如加上某个WIFI模块,会添加这个WIFI模块的驱动。你可以使用厂家的原始方案,或是使用/借鉴方案商的方案,做出一个“能用”的产品。② 基础驱动弱化;高级驱动专业化基础的驱动,比如GPIO、UART、SPI、I2C、LCD、MMC等,有了太多的书籍、视频、示例代码,修修改改总是可以用的。很多所谓的驱动工程师,实际上就是“调参工程师”。我们群里有名的火哥,提出了一个概念:这些驱动就起一个“hardware enable”的作用。高级的驱动,比如USB、PCIE、HDMI、MIPI、GPU、WIFI、蓝牙、摄像头、声卡。体系非常复杂,很少有人能讲清楚,很多时候只是一笔带过。配置一下应用层工具就了事,能用就成。这些高级驱动,工作中需要专门的人来负责,非常专业。他们是某一块的专家,比如摄像头专家、音频专家。③ 项目为王你到一个公司,目的是把产品做出来,会涉及APP到内核到驱动全流程。中小公司玩不起华为中兴的配置,需要的是全面手。大公司里,只负责很小很小一块的镙丝钉,位置也不太稳固啊。所以,如果你不是立志成为某方面的专家,那就做一个全栈工程师吧。④ 调试很重要都说代码是3分写7分调,各种调试调优技术,可以为你的升职加薪加一把火。基于上述4点,我录制的全新视频将有这些特点:1. 快速入门,2. 实战项目,3. 驱动大全,4. 专题,5. 授人以渔,6. 要做任务另外,我们会使用多款芯片同时录制,先讲通用的原理,再单独讲各个板子的操作。这些芯片涵盖主流芯片公司的主流芯片,让你学习工作无缝对接。1.快速入门入门讲究的是快速,入门之后再慢慢深入,特别是对于急着找工作的学生,对于业余时间挑灯夜读的工作了的人,一定要快!再从裸机、U-boot、内核、驱动这样的路线学习就不适合了,时间就拉得太长了。搞不好学了后面忘了前面。并且实际工作中并不需要你去弄懂U-boot,会用就行:U-boot比驱动还复杂。讲哪些内容?怎么讲呢?混着讲比如先讲LED APP,知道APP怎么调用驱动,再讲LED硬件原理和裸机,最后讲驱动的编写。这样可以快速掌握嵌入式Linux的整套开发流程,不必像以前那样光学习裸机就花上1、2个月。而里面的裸机课程,也会让你在掌握硬件操作的同时,把单片机也学会了。讲基础技能中断、休眠-唤醒、异步通知、阻塞、内存映射等等机制,会配合驱动和APP来讲解。这些技能是嵌入式Linux开发的基础。而这些驱动,只会涉及LED、按制、LCD等几个驱动。掌握了这些输入、输出的驱动和对应的APP后,你已经具备基本的开发能力了。讲配置我们从厂家、从方案公司基本上都可以拿到一套完整的开发环境,怎么去配置它?需要懂shell和python等配置脚本。效果效率优先以前我都是现场写代码、现场写文档,字写得慢,降低了学习效率。这次,效果与效率统一考虑,不再追求所有东西都现场写。容易的地方可先写好代码文档,难的地方现场写。2.实战项目会讲解这样的涉及linux网关/服务器相关项目(不限于,请多提建议):                   定位为:快速掌握项目开发经验,丰满简历。涉及的每一部分都会讲,比如如果涉及蓝牙,在这里只会讲怎么使用,让你能写出程序;如果要深入,可以看后面的蓝牙专题。3. 驱动大全包括基础驱动、高级驱动。这些驱动都是独立成章,深入讲解。虽然基础驱动弱化了,但是作为Linux系统开发人员,这是必备技能,并且从驱动去理解内核是一个好方法。在讲解这些驱动时,会把驱动的运行环境,比如内核调度,进程线程等概念也讲出来,这样就可以搭建一个知识体系。没有这些知识体系的话,对驱动的理解就太肤浅了,等于在Linux框架下写裸机,一叶障目,不见泰山。定位为:工具、字典,用到再学习。4. 专题想深入学习的任何内容,都可独立为专题。比如U-boot专题、内核内存管理专题、systemtap调试专题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值