原创:http://blog.sina.com.cn/u/2312748742
uboot就是在内核运行前的一段小程序,用来初始化硬件设备,建立内存空间映射图。从而将系统的软硬件带到合适的状态,主要功能就是为了启动内核,它将内核从flash中拷贝到ddr中,然后跳转到内核入口中,交由内核控制权,uboot严重依赖硬件,因此一个通用的uboot不太可能。
flash一般是依次存储 bootloader,boot启动参数,kernel,文件系统。
多阶段能实现更复杂的功能,可分stage1 和stage2;stage1完成初始化硬件,为stage2准备内存空间。
stage2初始化本阶段要使用的硬件设备,检测内存映射,将kernel和根文件系统映像拷贝到内存中,为内核设置启动参数。调用内核。主要由c语音实现。
1)board,和开发板相关的文件,每一个开发板都有自己的目录。
2)common,实现uboot命令行下支持的命令。每一个命令都一个文件。如bootm,对应cmd_bootm.c
3) cpu.与cpu架构相关的目录。
4)disk,对硬盘的支持。比较少用。一般都用flash
5) doc 文档目录
6)drivers uboot支持的设备驱动程序。比较重要的有网卡和串口等。
7)include uboot使用的头文件。还有对各种硬件平台支持的汇编文件,系统配置文件,文件系统支持的文件。还有该目录下的configs目录有与开发板相关的配置头文件。
8)net 与网络协议栈相关的代码
9)tools 生产uboot的工具。如mkimage ,crc等
这个汇编程序时UBoot 的入口程序,以复位向量开头:
reset
↓
cpu_init_crit
↓
relocate
↓
stack_setup
↓
start_armboot()
↓
init_sequence[]
↓
getenv()
↓
main_loop()
其中前面4步为Stage1,下面来详细分析一下 cpu/arm920t/start.S
这里以ARM9 2410 为例,2440移植时需要修改一些配置,具体的再后面的移植
中介绍.
.globl _start
_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
_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
.balignl 16,0xdeadbeef
_TEXT_BASE:
.word TEXT_BASE ;
定义一个字并分配空间 4bytes
.globl _armboot_start
_armboot_start:
.word _start ;
声明一个全局的,并用 _start 初始化它, 在u-boot.lds 中定义
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
reset:
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
//这段是关watchdog
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008
# define CLKDIVN 0x14800014
#else
# define pWTCON 0x53000000
# define INTMSK 0x4A000008
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014
# endif
ldr r0, =pWTCON //具体可以查看手册
mov r1, #0x0
str r1, [r0]
mov r1, #0xffffffff //禁止所有中断
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit ;cpu 初始化,其中会调用lowlevel_init.S
/
*********************************************************************
*********
* BL 为相对寻址,以程序计数器PC 的当前值为基地址,指令中的地址标号作
为偏移量,将两者相加之后得到操作数的有效地址
* ARM 指令集中的4 条跳转指令可以完成从当前指令向前或向后的32MB 的地
址空间的跳转,
* 用的是相对寻址,它们是:B、BL、BLX、BX
*********************************************************************
**********/
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:
adr r0, _start
/
*********************************************************************
*****
* 把_start 的相对地址移到r0, 相对寻址以程序计数器PC 的当前值为基地址,
* 指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。
* 它是与位置无关的,主要看Boot 在哪里运行,也就是PC 指针在哪里 (假设
_start 偏移量为0),
* 例如这段代码在 0x05000000 (FLASH 起始地址)运行,即此时
PC=0x05000000,
那么 adr r0, _start 得到 r0 = 0x05000000;
* 如果在地址 0x33008000(Boot 在RAM 中加载地址)运行,即此时
PC=0x33008000,那么r0 就是 0x33008000 了。
*通过adr指令得到当前代码的地址信息:如果U-boot 是从RAM 开始运行,
则从adr,r0,_start 得到的地址信息为
*r0=_start=_TEXT_BASE=TEXT_BASE=0x3ff80000;如果U-boot 从
Flash
开始运行,即从处理器对应的地址运 行,则*r0=0x0000,这时将会执行
copy_loop 标识的那段代码了
*********************************************************************
*****/
ldr r1, _TEXT_BASE
cmp r0, r1
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2
add r2, r0, r2
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
#endif
stack_setup:
ldr r0, _TEXT_BASE
sub r0, r0, #CFG_MALLOC_LEN
sub r0, r0, #CFG_GBL_DATA_SIZE
#ifdef CONFIG_USE_IRQ
sub r0, r0,
#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12
clear_bss:
ldr r0, _bss_start
ldr r1, _bss_end
mov r2, #0x00000000
clbss_l:
str r2, [r0]
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
/********************************************************************
跳转到 board_init_r:
一、linux系统分为:uboot,linux内核,文件系统,用户应用程序
二、uboot概念:
三、存储位置:
四、bootloader启动过程可分单阶段和多阶段。
将stage2复制到内存中,设置堆栈然后跳转到stage2中。主要由汇编实现。start.s函数
五、uboot代码架构:
六、u-boot.lds 链接顺序:
首先了解uboot 的链接脚本arch/arm/cpu/arm926ejs/u-boot.lds,它定义了目标程序
各部分的链接顺序。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-
littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
_end = .;
}
第一个链接的是cpu/board/start.o,也即Uboot 的入口指令在start 中,下面
详细分析程序的跳转和函数调用关系
各部分的链接顺序。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-
littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
_end = .;
}
第一个链接的是cpu/board/start.o,也即Uboot 的入口指令在start 中,下面
详细分析程序的跳转和函数调用关系
七、stage1,arch/arm/cpu/arm926ejs/start.S
这个汇编程序时UBoot 的入口程序,以复位向量开头:
reset
↓
cpu_init_crit
↓
relocate
↓
stack_setup
↓
start_armboot()
↓
init_sequence[]
↓
getenv()
↓
main_loop()
其中前面4步为Stage1,下面来详细分析一下 cpu/arm920t/start.S
这里以ARM9 2410 为例,2440移植时需要修改一些配置,具体的再后面的移植
中介绍.
.globl _start
_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
_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
.balignl 16,0xdeadbeef
_TEXT_BASE:
.word TEXT_BASE ;
定义一个字并分配空间 4bytes
.globl _armboot_start
_armboot_start:
.word _start ;
声明一个全局的,并用 _start 初始化它, 在u-boot.lds 中定义
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
reset:
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
//这段是关watchdog
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008
# define CLKDIVN 0x14800014
#else
# define pWTCON 0x53000000
# define INTMSK 0x4A000008
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014
# endif
ldr r0, =pWTCON //具体可以查看手册
mov r1, #0x0
str r1, [r0]
mov r1, #0xffffffff //禁止所有中断
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit ;cpu 初始化,其中会调用lowlevel_init.S
/
*********************************************************************
*********
* BL 为相对寻址,以程序计数器PC 的当前值为基地址,指令中的地址标号作
为偏移量,将两者相加之后得到操作数的有效地址
* ARM 指令集中的4 条跳转指令可以完成从当前指令向前或向后的32MB 的地
址空间的跳转,
* 用的是相对寻址,它们是:B、BL、BLX、BX
*********************************************************************
**********/
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:
adr r0, _start
/
*********************************************************************
*****
* 把_start 的相对地址移到r0, 相对寻址以程序计数器PC 的当前值为基地址,
* 指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。
* 它是与位置无关的,主要看Boot 在哪里运行,也就是PC 指针在哪里 (假设
_start 偏移量为0),
* 例如这段代码在 0x05000000 (FLASH 起始地址)运行,即此时
PC=0x05000000,
那么 adr r0, _start 得到 r0 = 0x05000000;
* 如果在地址 0x33008000(Boot 在RAM 中加载地址)运行,即此时
PC=0x33008000,那么r0 就是 0x33008000 了。
*通过adr指令得到当前代码的地址信息:如果U-boot 是从RAM 开始运行,
则从adr,r0,_start 得到的地址信息为
*r0=_start=_TEXT_BASE=TEXT_BASE=0x3ff80000;如果U-boot 从
Flash
开始运行,即从处理器对应的地址运 行,则*r0=0x0000,这时将会执行
copy_loop 标识的那段代码了
*********************************************************************
*****/
ldr r1, _TEXT_BASE
cmp r0, r1
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2
add r2, r0, r2
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
#endif
stack_setup:
ldr r0, _TEXT_BASE
sub r0, r0, #CFG_MALLOC_LEN
sub r0, r0, #CFG_GBL_DATA_SIZE
#ifdef CONFIG_USE_IRQ
sub r0, r0,
#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12
clear_bss:
ldr r0, _bss_start
ldr r1, _bss_end
mov r2, #0x00000000
clbss_l:
str r2, [r0]
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
/********************************************************************
八、stage2;arch/arm/lib/Board.c