一. uboot第一阶段初识

  1.1. 什么是uboot第一阶段

    1.1.1. 启动os三个阶段

      1.1.1.1. bl0阶段

        a. 这段代码是三星固化到iROM中,可以查看《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》

        b. 这段代码作用是将uboot第一阶段的8kb加载到iRAM中

      1.1.1.2. bl1阶段(uboot第一阶段)

        a. 此部分是整个uboot的前8k部分

        b. 此部分由bl0 加载到iRAM指定地址

      1.1.1.3. bl2阶段(整个uboot)

        a. 此部分是整个uboot    

        b. 此部分由bl1重定位到DDR的链接地址启动开始执行

        c. uboot的第二阶段就是要初始化bl1剩下的还没被初始化的硬件。主要是SoC外部硬件(譬如iNand、网卡芯片····)、uboot本身的一些东西(uboot的命令、环境变量等····)。然后最终初始化完必要的东西后进入uboot的命令行准备接受命令。

  1.2. 第一阶段主要作用

    a. 初始化DDR

    b. 将(bl2)整个uboot重定位到DDR中

    c. 跳转到DDR中执行uboot(长跳转)

二. uboot 第一阶段源码分析

  2.1. uboot链接脚本分析

    a. ENTRY(_start):整个程序的入口取决于链接脚本中ENTRY声明的地方。ENTRY(_start)因此_start符号所在的文件就是整个程序的起始文件,_start所在处的代码就是整个程序的起始代码。

    b. 在text段中,指定很多文件的段靠前存放,这样可以保证必要的文件可以在uboot前8K地址内     

  1. OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
  2. /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
  3. OUTPUT_ARCH(arm)
  4. ENTRY(_start)
  5. SECTIONS
  6. {
  7. . = 0x00000000;
  8.  
  9. . = ALIGN();
  10. .text :
  11. {
  12. cpu/s5pc11x/start.o (.text)
  13. cpu/s5pc11x/s5pc110/cpu_init.o (.text)
  14. board/samsung/x210/lowlevel_init.o (.text)
  15. cpu/s5pc11x/onenand_cp.o (.text)
  16. cpu/s5pc11x/nand_cp.o (.text)
  17. cpu/s5pc11x/movi.o (.text)
  18. common/secure_boot.o (.text)
  19. common/ace_sha1.o (.text)
  20. cpu/s5pc11x/pmic.o (.text)
  21. *(.text)
  22. }
  23.  
  24. . = ALIGN();
  25. .rodata : { *(.rodata) }
  26.  
  27. . = ALIGN();
  28. .data : { *(.data) }
  29.  
  30. . = ALIGN();
  31. .got : { *(.got) }
  32.  
  33. __u_boot_cmd_start = .;
  34. .u_boot_cmd : { *(.u_boot_cmd) }
  35. __u_boot_cmd_end = .;
  36.  
  37. . = ALIGN();
  38. .mmudata : { *(.mmudata) }
  39.  
  40. . = ALIGN();
  41. __bss_start = .;
  42. .bss : { *(.bss) }
  43. _end = .;
  44. }

  2.2. start.S分析 

    2.2.1. 相关头文件分析 

      a. 有些头文件是在配置/编译过程生成的

      b. 有些头文件使用了符号链接

      c. 很多宏定义在x210_sd.h宏定义,但此文件被config.h所引用

  1. #include <config.h>
  2. #include <version.h>
  3. #if defined(CONFIG_ENABLE_MMU)
  4. #include <asm/proc/domain.h>
  5. #endif
  6. #include <regs.h>

    2.2.2. uboot头信息地址占位

      a. 定义4个字空间占用16字节,16字节信息定义可以查看 《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》   

      b. 此处仅仅是定义并未赋有效值,有效值再制作usb启动uboot是写入(如使用sd_fusing中sd_fdisk.c文件会填充)

  1. #if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
  2. .word 0x2000
  3. .word 0x0
  4. .word 0x0
  5. .word 0x0
  6. #endif

    2.2.3. _start汇编标号分析

      2.3.1.1. 上述我们已经分析了,启动bl1时的起点就是_start

      2.3.1.2. b reset为什么开始执行的第一句汇编

        a. 无论是复位还是开启都属于重启,故启动先执行reset很合理

        b. reset后cpu处于SVC模式,reset汇编重新设置模式也无妨

  1. reset:
  2. /*
  3. * set the cpu to SVC32 mode and IRQ & FIQ disable
  4. */
  5. @;mrs r0,cpsr
  6. @;bic r0,r0,#0x1f
  7. @;orr r0,r0,#0xd3
  8. @;msr cpsr,r0
  9. msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC

    2.2.4. 设置栈到iRAM

      a. 此时DDR未初始化,但后面需要压栈出栈故此时把栈设置到iRAM

      b. 栈地址使用《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》memery map推荐的栈地址

  1. /*
  2. * Go setup Memory and board specific bits prior to relocation.
  3. */
  4.  
  5. ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */
  6. sub sp, sp, # /* set stack */
  7. mov fp, #

    2.2.5. lowlevel_init分析

      2.2.5.1. _TEXT_BASE

        2.2.5.1.1. 此标号相对应变量,变量值为TEXT_BASE

        2.2.5.1.2. TEXT_BASE是怎么来的

          a. 该变量值是make x210_sd_config配置时写入到config.mk文件中

          b. 该值会在\uboot\config.mk中作为uboot链接地址

  1. x210_sd_config : unconfig
  2. @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
  3. @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk

      2.2.5.2. 判断当前代码执行位置

        a. 这几行代码的作用就是判定当前代码执行的位置在SRAM中还是在DDR中。为什么要做这个判定?

          原因1:BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷启动那么当前代码应该是在SRAM中运行的BL1,如果是低功耗状态的复位这时候应该就是在DDR中运行的。

          原因2:我们判定当前运行代码的地址是有用的,可以指导后面代码的运行。譬如在lowlevel_init.S中判定当前代码的运行地址,就是为了确定要不要执行时钟初始化和初始化DDR的代码。如果当前代码是在SRAM中,说明冷启动,那么时钟和DDR都需要初始化;如果当前代码是在DDR中,那么说明是热启动则时钟和DDR都不用再次初始化。

        b. bic r1, pc, r0 这句代码的意义是:将pc的值中的某些bit位清0,剩下一些特殊的bit位赋值给r1(r0中为1的那些位清零)相等于:r1 = pc & ~(ff000fff)
ldr r2, _TEXT_BASE 加载链接地址到r2,然后将r2的相应位清0剩下特定位。
        c. 最后比较r1和r2.

  1. ldr r0, =0xff000fff
  2. bic r1, pc, r0 /* r0 <- current base addr of code */
  3. ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
  4. bic r2, r2, r0 /* r0 <- current base addr of code */
  5. cmp r1, r2 /* compare r0, r1 */
  6. beq 1f /* r0 == r1 then skip sdram init */

      2.2.5.3. lowlevel_init总结 

        a. 检查复位状态、IO恢复、关看门狗、开发板供电锁存、时钟初始化、DDR初始化、串口初始化并打印'O'、tzpc初始化、打印'K'。

        b. 其中值得关注的:关看门狗、开发板供电锁存、时钟初始化、DDR初始化、打印"OK"

  1. _TEXT_BASE:
  2. .word TEXT_BASE
  3.  
  4. .globl lowlevel_init
  5. lowlevel_init:
  6. push {lr}
  7.  
  8. /* check reset status */
  9.  
  10. ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
  11. ldr r1, [r0]
  12. bic r1, r1, #0xfff6ffff
  13. cmp r1, #0x10000
  14. beq wakeup_reset_pre
  15. cmp r1, #0x80000
  16. beq wakeup_reset_from_didle
  17.  
  18. /* IO Retention release */
  19. ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
  20. ldr r1, [r0]
  21. ldr r2, =IO_RET_REL
  22. orr r1, r1, r2
  23. str r1, [r0]
  24.  
  25. /* Disable Watchdog */
  26. ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
  27. mov r1, #
  28. str r1, [r0]
  29.  
  30. /* SRAM(2MB) init for SMDKC110 */
  31. /* GPJ1 SROM_ADDR_16to21 */
  32. ldr r0, =ELFIN_GPIO_BASE
  33.  
  34. ldr r1, [r0, #GPJ1CON_OFFSET]
  35. bic r1, r1, #0xFFFFFF
  36. ldr r2, =0x444444
  37. orr r1, r1, r2
  38. str r1, [r0, #GPJ1CON_OFFSET]
  39.  
  40. ldr r1, [r0, #GPJ1PUD_OFFSET]
  41. ldr r2, =0x3ff
  42. bic r1, r1, r2
  43. str r1, [r0, #GPJ1PUD_OFFSET]
  44.  
  45. /* GPJ4 SROM_ADDR_16to21 */
  46. ldr r1, [r0, #GPJ4CON_OFFSET]
  47. bic r1, r1, #(0xf<<)
  48. ldr r2, =(0x4<<)
  49. orr r1, r1, r2
  50. str r1, [r0, #GPJ4CON_OFFSET]
  51.  
  52. ldr r1, [r0, #GPJ4PUD_OFFSET]
  53. ldr r2, =(0x3<<)
  54. bic r1, r1, r2
  55. str r1, [r0, #GPJ4PUD_OFFSET]
  56.  
  57. /* CS0 - 16bit sram, enable nBE, Byte base address */
  58. ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */
  59. mov r1, #0x1
  60. str r1, [r0]
  61.  
  62. /* PS_HOLD pin(GPH0_0) set to high */
  63. ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
  64. ldr r1, [r0]
  65. orr r1, r1, #0x300
  66. orr r1, r1, #0x1
  67. str r1, [r0]
  68.  
  69. /* when we already run in ram, we don't need to relocate U-Boot.
  70. * and actually, memory controller must be configured before U-Boot
  71. * is running in ram.
  72. */
  73. ldr r0, =0xff000fff
  74. bic r1, pc, r0 /* r0 <- current base addr of code */
  75. ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
  76. bic r2, r2, r0 /* r0 <- current base addr of code */
  77. cmp r1, r2 /* compare r0, r1 */
  78. beq 1f /* r0 == r1 then skip sdram init */
  79.  
  80. /* init system clock */
  81. bl system_clock_init
  82.  
  83. /* Memory initialize */
  84. bl mem_ctrl_asm_init
  85.  
  86. :
  87. /* for UART */
  88. bl uart_asm_init
  89.  
  90. bl tzpc_init
  91.  
  92. #if defined(CONFIG_ONENAND)
  93. bl onenandcon_init
  94. #endif
  95.  
  96. #if defined(CONFIG_NAND)
  97. /* simple init for NAND */
  98. bl nand_asm_init
  99. #endif
  100.  
  101. /* check reset status */
  102.  
  103. ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
  104. ldr r1, [r0]
  105. bic r1, r1, #0xfffeffff
  106. cmp r1, #0x10000
  107. beq wakeup_reset_pre
  108.  
  109. /* ABB disable */
  110. ldr r0, =0xE010C300
  111. orr r1, r1, #(0x1<<)
  112. str r1, [r0]
  113.  
  114. /* Print 'K' */
  115. ldr r0, =ELFIN_UART_CONSOLE_BASE
  116. ldr r1, =0x4b4b4b4b
  117. str r1, [r0, #UTXH_OFFSET]
  118.  
  119. pop {pc}

   2.2.6. uboot重定位详解   

    a. 真正的重定位是通过调用movi_bl2_copy函数完成的,在uboot/cpu/s5pc11x/movi.c中。是一个C语言的函数

    b. copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,CFG_PHY_UBOOT_BASE, 0);
      分析参数:2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的开始扇区,这个扇区数字必须和烧录uboot时烧录的位置相同;MOVI_BL2_BLKCNT是uboot的长度占用的扇区数;CFG_PHY_UBOOT_BASE是重定位时将uboot的第二部分复制到DDR中的起始地址(33E00000).

  1. ldr r0, =INF_REG_BASE
  2. ldr r1, [r0, #INF_REG3_OFFSET]
  3. cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */
  4. beq nand_boot
  5. cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */
  6. beq onenand_boot
  7. cmp r1, #BOOT_MMCSD
  8. beq mmcsd_boot
  9. cmp r1, #BOOT_NOR
  10. beq nor_boot
  11. cmp r1, #BOOT_SEC_DEV
  12. beq mmcsd_boot
  13.  
  14. nand_boot:
  15. mov r0, #0x1000
  16. bl copy_from_nand
  17. b after_copy
  18.  
  19. onenand_boot:
  20. bl onenand_bl2_copy
  21. b after_copy
  22.  
  23. mmcsd_boot:
  24. #if DELETE
  25. ldr sp, _TEXT_PHY_BASE
  26. sub sp, sp, #
  27. mov fp, #
  28. #endif
  29. bl movi_bl2_copy
  30. b after_copy
  31.  
  32. nor_boot:
  33. bl read_hword
  34. b after_copy

    2.2.7. 设置MMU

      a. MMU就是memory management unit,内存管理单元。MMU实际上是SOC中一个硬件单元,它的主要功能就是实现虚拟地址到物理地址的映射。

      b. MMU单片在CP15协处理器中进行控制,也就是说要操控MMU进行虚拟地址映射,方法就是对cp15协处理器的寄存器进行编程。

      c. MMU的作用

        (1)访问控制就是:在管理上对内存进行分块,然后每块进行独立的虚拟地址映射,然后在每一块的映射关系中同时还实现了访问控制(对该块可读、可写、只读、只写、不可访问等控制)

        (2)回想在C语言中编程中经常会出现一个错误:Segmentation fault。实际上这个段错误就和MMU实现的访问控制有关。当前程序只能操作自己有权操作的地址范围(若干个内存块),如果当前程序指针出错访问了不该访问的内存块则就会触发段错误。

  1. after_copy:
  2.  
  3. #if defined(CONFIG_ENABLE_MMU)
  4. enable_mmu:
  5. /* enable domain access */
  6. ldr r5, =0x0000ffff
  7. mcr p15, , r5, c3, c0, @load domain access register
  8.  
  9. /* Set the TTB register */
  10. ldr r0, _mmu_table_base
  11. ldr r1, =CFG_PHY_UBOOT_BASE
  12. ldr r2, =0xfff00000
  13. bic r0, r0, r2
  14. orr r1, r0, r1
  15. mcr p15, , r1, c2, c0,
  16.  
  17. /* Enable the MMU */
  18. mmu_on:
  19. mrc p15, , r0, c1, c0,
  20. orr r0, r0, #
  21. mcr p15, , r0, c1, c0,
  22. nop
  23. nop
  24. nop
  25. nop
  26. #endif

    2.2.8. 再次设置栈,清理bss

      a. 第三次设置栈。这次设置栈还是在DDR中,之前虽然已经在DDR中设置过一次栈了,但是本次设置栈的目的是将栈放在比较合适(安全,紧凑而不浪费内存)的地方。

      b. 我们实际将栈设置在uboot起始地址上方2MB处,这样安全的栈空间是:2MB-uboot大小-0x1000=1.8MB左右。这个空间既没有太浪费内存,又足够安全。

      c. 清理bss段代码和裸机中讲的一样。注意表示bss段的开头和结尾地址的符号是从链接脚本u-boot.lds得来的。

  1. stack_setup:
  2. #if defined(CONFIG_MEMORY_UPPER_CODE)
  3. ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
  4. #else
  5. ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
  6. sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
  7. sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
  8. #if defined(CONFIG_USE_IRQ)
  9. sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
  10. #endif
  11. sub sp, r0, # /* leave 3 words for abort-stack */
  12.  
  13. #endif
  14.  
  15. clear_bss:
  16. ldr r0, _bss_start /* find start of bss segment */
  17. ldr r1, _bss_end /* stop here */
  18. mov r2, #0x00000000 /* clear */

    2.2.9. 开启uboot第二阶段

      a. start_armboot是uboot/lib_arm/board.c中,这是一个C语言实现的函数。这个函数就是uboot的第二阶段。这句代码的作用就是将uboot第二阶段执行的函数的地址传给pc,实际上就是使用一个远跳转直接跳转到DDR中的第二阶段开始地址处。

      b. 远跳转的含义就是这句话加载的地址和当前运行地址无关,而和链接地址有关。因此这个远跳转可以实现从SRAM中的第一阶段跳转到DDR中的第二阶段。
      c. 这里这个远跳转就是uboot第一阶段和第二阶段的分界线。

  1. clbss_l:
  2. str r2, [r0] /* clear loop... */
  3. add r0, r0, #
  4. cmp r0, r1
  5. ble clbss_l
  6.  
  7. ldr pc, _start_armboot
  8.  
  9. _start_armboot:
  10. .word start_armboot

uboot启动第一阶段分析的更多相关文章

  1. u-boot启动第一阶段

    目标板:2440开发板 u-boot启动的第一阶段是在文件start.S中完成的,这个过程对不同硬件平台的设置是不同的.下面进入start.S _start: b reset //跳转到reset / ...

  2. 嵌入式Linux驱动学习之路(五)u-boot启动流程分析

    这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...

  3. (转载)U-boot启动完全分析

    1.1 U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 Ø 硬件设备初始化 Ø 加载U-Boot第二阶段代码到RAM空间 Ø 设置好栈 Ø ...

  4. U-Boot启动过程完全分析

    U-Boot启动过程完全分析 1.1       U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 硬件设备初始化 加载U-Boot第二阶段 ...

  5. 嵌入式Linux开发之uboot启动Linux整体流程分析

    嵌入式Linux开发之uboot启动Linux整体流程分析 Uboot全称Universal Boot Loader,一个遵循GPL协议的的开源项目,其作用是引导操作系统,支持引导linux.VxWo ...

  6. U-Boot启动过程完全分析<转>

    转载自:http://www.cnblogs.com/heaad/archive/2010/07/17/1779829.html 1.1       U-Boot工作过程 U-Boot启动内核的过程可 ...

  7. imx6 uboot启动流程分析

    参考http://blog.csdn.net/skyflying2012/article/details/25804209 这里以imx6平台为例,分析uboot启动流程对于任何程序,入口函数是在链接 ...

  8. 【ARM-Linux开发】U-Boot启动过程--详细版的完全分析

    ---------------------------------------------------------------------------------------------------- ...

  9. uboot启动 及命令分析(3)

    u-boot命令 先贴一个重要结构,位于uboot/include/command.h,这个结构代表每个uboot命令 struct cmd_tbl_s { char     *name;   /* ...

随机推荐

  1. electron启动出现短暂的白屏

    mainWindow = new BrowserWindow({ height: 600, width: 960, frame: false, minWidth: 710, minHeight: 50 ...

  2. Futures工具类使用

    Futures是guava提供的工具类,全类名是com.google.common.util.concurrent.Futures.配合MoreExecutors使用,效果极佳. 主要方法如下: 1. ...

  3. Android 通过应用设置系统日期和时间的方法

    Android 通过应用设置系统日期和时间的方法 android 2.3 android 4.0 测试可行,不过需要ROOT权限. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...

  4. NOIP2002-字串变换【双向BFS】

    NOIP2002-字串变换 Description 已知有两个字串A,BA,B及一组字串变换的规则(至多66个规则): A_1A1​ ->B_1B1​ A_2A2​ -> B_2B2​ 规 ...

  5. yii2.0 curd操作数据写法

    一.执行原生sql查询,创建yii\db\Command         insert(),update(),delete()直接构建,相应的sql语句 查: 1.查询一条 \Yii::$app-&g ...

  6. spring boot: Whitelabel Error Page的解决方案

    http://blog.csdn.net/u014788227/article/details/53670112

  7. MySQL中concat以及group_concat的使用

    摘自:https://www.jianshu.com/p/43cb4c5d33c1 说明: 本文中使用的例子均在下面的数据库表tt2下执行: 一.concat()函数 1.功能:将多个字符串连接成一个 ...

  8. JavaScript json loop item in array

    Iterating through/Parsing JSON Object via JavaScript 解答1 Your JSON object is incorrect because it ha ...

  9. webrtc相关概念

    SDP Session Description Protocol Session Traversal Utilities for NAT(STUN)Traversal Using Relays aro ...

  10. JRE、JDK、JVM 及 JIT 之间有什么不同

    java虚拟机(JVM)     使用java编程语言的主要优势就是平台的独立性.你曾经想知道过java怎么实现平台的独立性吗?对,就是虚拟机,它抽象化了硬件设备,开发者和他们的程序的得以操作系统.虚 ...