uboot启动第一阶段分析
一. 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地址内
- OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
- /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
- OUTPUT_ARCH(arm)
- ENTRY(_start)
- SECTIONS
- {
- . = 0x00000000;
- . = ALIGN();
- .text :
- {
- cpu/s5pc11x/start.o (.text)
- cpu/s5pc11x/s5pc110/cpu_init.o (.text)
- board/samsung/x210/lowlevel_init.o (.text)
- cpu/s5pc11x/onenand_cp.o (.text)
- cpu/s5pc11x/nand_cp.o (.text)
- cpu/s5pc11x/movi.o (.text)
- common/secure_boot.o (.text)
- common/ace_sha1.o (.text)
- cpu/s5pc11x/pmic.o (.text)
- *(.text)
- }
- . = ALIGN();
- .rodata : { *(.rodata) }
- . = ALIGN();
- .data : { *(.data) }
- . = ALIGN();
- .got : { *(.got) }
- __u_boot_cmd_start = .;
- .u_boot_cmd : { *(.u_boot_cmd) }
- __u_boot_cmd_end = .;
- . = ALIGN();
- .mmudata : { *(.mmudata) }
- . = ALIGN();
- __bss_start = .;
- .bss : { *(.bss) }
- _end = .;
- }
2.2. start.S分析
2.2.1. 相关头文件分析
a. 有些头文件是在配置/编译过程生成的
b. 有些头文件使用了符号链接
c. 很多宏定义在x210_sd.h宏定义,但此文件被config.h所引用
- #include <config.h>
- #include <version.h>
- #if defined(CONFIG_ENABLE_MMU)
- #include <asm/proc/domain.h>
- #endif
- #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文件会填充)
- #if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
- .word 0x2000
- .word 0x0
- .word 0x0
- .word 0x0
- #endif
2.2.3. _start汇编标号分析
2.3.1.1. 上述我们已经分析了,启动bl1时的起点就是_start
2.3.1.2. b reset为什么开始执行的第一句汇编
a. 无论是复位还是开启都属于重启,故启动先执行reset很合理
b. reset后cpu处于SVC模式,reset汇编重新设置模式也无妨
- reset:
- /*
- * set the cpu to SVC32 mode and IRQ & FIQ disable
- */
- @;mrs r0,cpsr
- @;bic r0,r0,#0x1f
- @;orr r0,r0,#0xd3
- @;msr cpsr,r0
- 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推荐的栈地址
- /*
- * Go setup Memory and board specific bits prior to relocation.
- */
- ldr sp, =0xd0036000 /* end of sram dedicated to u-boot */
- sub sp, sp, # /* set stack */
- 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链接地址
- x210_sd_config : unconfig
- @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
- @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.
- ldr r0, =0xff000fff
- bic r1, pc, r0 /* r0 <- current base addr of code */
- ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
- bic r2, r2, r0 /* r0 <- current base addr of code */
- cmp r1, r2 /* compare r0, r1 */
- beq 1f /* r0 == r1 then skip sdram init */
2.2.5.3. lowlevel_init总结
a. 检查复位状态、IO恢复、关看门狗、开发板供电锁存、时钟初始化、DDR初始化、串口初始化并打印'O'、tzpc初始化、打印'K'。
b. 其中值得关注的:关看门狗、开发板供电锁存、时钟初始化、DDR初始化、打印"OK"
- _TEXT_BASE:
- .word TEXT_BASE
- .globl lowlevel_init
- lowlevel_init:
- push {lr}
- /* check reset status */
- ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
- ldr r1, [r0]
- bic r1, r1, #0xfff6ffff
- cmp r1, #0x10000
- beq wakeup_reset_pre
- cmp r1, #0x80000
- beq wakeup_reset_from_didle
- /* IO Retention release */
- ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
- ldr r1, [r0]
- ldr r2, =IO_RET_REL
- orr r1, r1, r2
- str r1, [r0]
- /* Disable Watchdog */
- ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
- mov r1, #
- str r1, [r0]
- /* SRAM(2MB) init for SMDKC110 */
- /* GPJ1 SROM_ADDR_16to21 */
- ldr r0, =ELFIN_GPIO_BASE
- ldr r1, [r0, #GPJ1CON_OFFSET]
- bic r1, r1, #0xFFFFFF
- ldr r2, =0x444444
- orr r1, r1, r2
- str r1, [r0, #GPJ1CON_OFFSET]
- ldr r1, [r0, #GPJ1PUD_OFFSET]
- ldr r2, =0x3ff
- bic r1, r1, r2
- str r1, [r0, #GPJ1PUD_OFFSET]
- /* GPJ4 SROM_ADDR_16to21 */
- ldr r1, [r0, #GPJ4CON_OFFSET]
- bic r1, r1, #(0xf<<)
- ldr r2, =(0x4<<)
- orr r1, r1, r2
- str r1, [r0, #GPJ4CON_OFFSET]
- ldr r1, [r0, #GPJ4PUD_OFFSET]
- ldr r2, =(0x3<<)
- bic r1, r1, r2
- str r1, [r0, #GPJ4PUD_OFFSET]
- /* CS0 - 16bit sram, enable nBE, Byte base address */
- ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */
- mov r1, #0x1
- str r1, [r0]
- /* PS_HOLD pin(GPH0_0) set to high */
- ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
- ldr r1, [r0]
- orr r1, r1, #0x300
- orr r1, r1, #0x1
- str r1, [r0]
- /* when we already run in ram, we don't need to relocate U-Boot.
- * and actually, memory controller must be configured before U-Boot
- * is running in ram.
- */
- ldr r0, =0xff000fff
- bic r1, pc, r0 /* r0 <- current base addr of code */
- ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
- bic r2, r2, r0 /* r0 <- current base addr of code */
- cmp r1, r2 /* compare r0, r1 */
- beq 1f /* r0 == r1 then skip sdram init */
- /* init system clock */
- bl system_clock_init
- /* Memory initialize */
- bl mem_ctrl_asm_init
- :
- /* for UART */
- bl uart_asm_init
- bl tzpc_init
- #if defined(CONFIG_ONENAND)
- bl onenandcon_init
- #endif
- #if defined(CONFIG_NAND)
- /* simple init for NAND */
- bl nand_asm_init
- #endif
- /* check reset status */
- ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
- ldr r1, [r0]
- bic r1, r1, #0xfffeffff
- cmp r1, #0x10000
- beq wakeup_reset_pre
- /* ABB disable */
- ldr r0, =0xE010C300
- orr r1, r1, #(0x1<<)
- str r1, [r0]
- /* Print 'K' */
- ldr r0, =ELFIN_UART_CONSOLE_BASE
- ldr r1, =0x4b4b4b4b
- str r1, [r0, #UTXH_OFFSET]
- 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).
- ldr r0, =INF_REG_BASE
- ldr r1, [r0, #INF_REG3_OFFSET]
- cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */
- beq nand_boot
- cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */
- beq onenand_boot
- cmp r1, #BOOT_MMCSD
- beq mmcsd_boot
- cmp r1, #BOOT_NOR
- beq nor_boot
- cmp r1, #BOOT_SEC_DEV
- beq mmcsd_boot
- nand_boot:
- mov r0, #0x1000
- bl copy_from_nand
- b after_copy
- onenand_boot:
- bl onenand_bl2_copy
- b after_copy
- mmcsd_boot:
- #if DELETE
- ldr sp, _TEXT_PHY_BASE
- sub sp, sp, #
- mov fp, #
- #endif
- bl movi_bl2_copy
- b after_copy
- nor_boot:
- bl read_hword
- 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实现的访问控制有关。当前程序只能操作自己有权操作的地址范围(若干个内存块),如果当前程序指针出错访问了不该访问的内存块则就会触发段错误。
- after_copy:
- #if defined(CONFIG_ENABLE_MMU)
- enable_mmu:
- /* enable domain access */
- ldr r5, =0x0000ffff
- mcr p15, , r5, c3, c0, @load domain access register
- /* Set the TTB register */
- ldr r0, _mmu_table_base
- ldr r1, =CFG_PHY_UBOOT_BASE
- ldr r2, =0xfff00000
- bic r0, r0, r2
- orr r1, r0, r1
- mcr p15, , r1, c2, c0,
- /* Enable the MMU */
- mmu_on:
- mrc p15, , r0, c1, c0,
- orr r0, r0, #
- mcr p15, , r0, c1, c0,
- nop
- nop
- nop
- nop
- #endif
2.2.8. 再次设置栈,清理bss
a. 第三次设置栈。这次设置栈还是在DDR中,之前虽然已经在DDR中设置过一次栈了,但是本次设置栈的目的是将栈放在比较合适(安全,紧凑而不浪费内存)的地方。
b. 我们实际将栈设置在uboot起始地址上方2MB处,这样安全的栈空间是:2MB-uboot大小-0x1000=1.8MB左右。这个空间既没有太浪费内存,又足够安全。
c. 清理bss段代码和裸机中讲的一样。注意表示bss段的开头和结尾地址的符号是从链接脚本u-boot.lds得来的。
- stack_setup:
- #if defined(CONFIG_MEMORY_UPPER_CODE)
- ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
- #else
- ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
- sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
- sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
- #if defined(CONFIG_USE_IRQ)
- sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
- #endif
- sub sp, r0, # /* leave 3 words for abort-stack */
- #endif
- clear_bss:
- ldr r0, _bss_start /* find start of bss segment */
- ldr r1, _bss_end /* stop here */
- 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第一阶段和第二阶段的分界线。
- clbss_l:
- str r2, [r0] /* clear loop... */
- add r0, r0, #
- cmp r0, r1
- ble clbss_l
- ldr pc, _start_armboot
- _start_armboot:
- .word start_armboot
uboot启动第一阶段分析的更多相关文章
- u-boot启动第一阶段
目标板:2440开发板 u-boot启动的第一阶段是在文件start.S中完成的,这个过程对不同硬件平台的设置是不同的.下面进入start.S _start: b reset //跳转到reset / ...
- 嵌入式Linux驱动学习之路(五)u-boot启动流程分析
这里说的u-boot启动流程,值得是从上电开机执行u-boot,到u-boot,到u-boot加载操作系统的过程.这一过程可以分为两个过程,各个阶段的功能如下. 第一阶段的功能: 硬件设备初始化. 加 ...
- (转载)U-boot启动完全分析
1.1 U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 Ø 硬件设备初始化 Ø 加载U-Boot第二阶段代码到RAM空间 Ø 设置好栈 Ø ...
- U-Boot启动过程完全分析
U-Boot启动过程完全分析 1.1 U-Boot工作过程 U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下: (1)第一阶段的功能 硬件设备初始化 加载U-Boot第二阶段 ...
- 嵌入式Linux开发之uboot启动Linux整体流程分析
嵌入式Linux开发之uboot启动Linux整体流程分析 Uboot全称Universal Boot Loader,一个遵循GPL协议的的开源项目,其作用是引导操作系统,支持引导linux.VxWo ...
- U-Boot启动过程完全分析<转>
转载自:http://www.cnblogs.com/heaad/archive/2010/07/17/1779829.html 1.1 U-Boot工作过程 U-Boot启动内核的过程可 ...
- imx6 uboot启动流程分析
参考http://blog.csdn.net/skyflying2012/article/details/25804209 这里以imx6平台为例,分析uboot启动流程对于任何程序,入口函数是在链接 ...
- 【ARM-Linux开发】U-Boot启动过程--详细版的完全分析
---------------------------------------------------------------------------------------------------- ...
- uboot启动 及命令分析(3)
u-boot命令 先贴一个重要结构,位于uboot/include/command.h,这个结构代表每个uboot命令 struct cmd_tbl_s { char *name; /* ...
随机推荐
- electron启动出现短暂的白屏
mainWindow = new BrowserWindow({ height: 600, width: 960, frame: false, minWidth: 710, minHeight: 50 ...
- Futures工具类使用
Futures是guava提供的工具类,全类名是com.google.common.util.concurrent.Futures.配合MoreExecutors使用,效果极佳. 主要方法如下: 1. ...
- Android 通过应用设置系统日期和时间的方法
Android 通过应用设置系统日期和时间的方法 android 2.3 android 4.0 测试可行,不过需要ROOT权限. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
- NOIP2002-字串变换【双向BFS】
NOIP2002-字串变换 Description 已知有两个字串A,BA,B及一组字串变换的规则(至多66个规则): A_1A1 ->B_1B1 A_2A2 -> B_2B2 规 ...
- yii2.0 curd操作数据写法
一.执行原生sql查询,创建yii\db\Command insert(),update(),delete()直接构建,相应的sql语句 查: 1.查询一条 \Yii::$app-&g ...
- spring boot: Whitelabel Error Page的解决方案
http://blog.csdn.net/u014788227/article/details/53670112
- MySQL中concat以及group_concat的使用
摘自:https://www.jianshu.com/p/43cb4c5d33c1 说明: 本文中使用的例子均在下面的数据库表tt2下执行: 一.concat()函数 1.功能:将多个字符串连接成一个 ...
- JavaScript json loop item in array
Iterating through/Parsing JSON Object via JavaScript 解答1 Your JSON object is incorrect because it ha ...
- webrtc相关概念
SDP Session Description Protocol Session Traversal Utilities for NAT(STUN)Traversal Using Relays aro ...
- JRE、JDK、JVM 及 JIT 之间有什么不同
java虚拟机(JVM) 使用java编程语言的主要优势就是平台的独立性.你曾经想知道过java怎么实现平台的独立性吗?对,就是虚拟机,它抽象化了硬件设备,开发者和他们的程序的得以操作系统.虚 ...