U-boot的启动流程
u-boot的作用主要是初始化一部分硬件,设置堆栈,然后把U-boot自身代码搬运到sdram当中运行.
U-BOOT的启动分为两个部分.
第一部分
(1) 初始化一部分硬件,比如关闭watch dog,中断等
(2) 搬运u-boot自身代码到sdram中
(3) 设置堆栈sp
(4) 跳到启动代码的第二部分开始执行(C语言部分)
第二部分
(1) 初始化需要用到的硬件,比如tty,flash,i2c总线等。
(2) 加载kernel到sdram中。
(3) 设置内核启动的参数。
(4) 调用内核。
总的来说,分为这几个步骤,调用内核后,就会开始执行内核部分代码。
系统上电后会从0x00000000地址处执行代码
在cpu的目录下,有几种类型的的CPU,比如arm920T,arm720T等,我使用的开发板为s3c2440,为CPU为arm920T,在arm920T目录下有个uboot的链接文件u-boot.lds.其代码如下
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
board/samsung/Ethan2440/lowlevel_init.o (.text)
board/samsung/Ethan2440/nand_read.o (.text)
*(.text)
}
...............
}
这里面的地址有两个,一个是链接文件(u-boot.lds)中的开始地址是0x0,代码段应该是从0地址开始运行,另一个\board\samsung\Ethan2440\config.mk中的代码段的链接地址。查看system.map可以看出代码段是0x33f80000开始链接的,那么代码怎么会在0x0中运行?查了一些资料,个人理解如下:如果理解透彻的可以讨论一下,这里面的使用的跳转指令ldr,bl,adr应该和地址是无关的,是基于PC指针偏移的,start.s,lowlevel_init.s,nand_read.s才参在系统刚启动的时候代码段得以运行,从而实现把"自身"搬运到0x33f80000的地址处。
globl _start
/*跳到start_code处开始执行代码.使用b跳转后,是不会再跳回来了*/
start: b start_code
/*把标号放到pc寄存器中执行*/
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
/* 定义一个4字节的函数名来初始,比如undefined_instuction,下面是个函数名,同理其它的也是一样. */
.balignl 16,0xdeadbeef
这里面的汇编使用采用AT&T语法,globl是linux下汇编的关键字, .global关键字用来让一个符号对链接器可见,可以供其他链接对象模块使用._start就是一个标号,也可以说是一个地址.
/*
*_TEXT_BASE用TEXT_BASE来初始化, TEXT_BASE的值是多少呢,经过自己查找,在board/Samsung/smdk2410下的config.mk, TEXT_BASE = 0x33F80000。
*/
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
/*
*_bss_start,_bss_end初始化,它们在哪儿定义,它们的值是多少呢,可以在cpu/arm920t/u-boot.lds中查到.它的值是多少呢,自己计算吧.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
(1)下面是代码真正开始执行的部分,上面是一些变量的定义及初始化
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
第1行mrs,cpsr是什么,查一下汇编指令, MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中,这里面的程序状态寄存器可以是cpsr,spsr.所以这里面,是把cpsr的值放到了r0中.
第2行bic指令是把r0与#0x1f取反后相与,也就是把后5位清零.可以查一下bic指令的用法.
第3行orr指令是把r0与#0xd3相或,前面已经把后5位清0,这里面后5位就是0xd3了.
第4行msr和mrs相对应的,把r0的值传送到cpsr中,这四句代码到底是做什么的,看上面的注释就知道是把cpu设置成管理模式的,哪怎么设置的呢,自己查的,你也可以查
CPSR格式如下所示。SPSR和CPSR格式相同。
31 30 29 28 27 26 7 6 5 4 3 2 1 0
N Z C V Q DNM(RAZ) I F T M4 M3 M2 M1 M0
这里面我们重点关注后面8位,因为我们的是0xd3,也就11010011,对应上面的,第I位和F位置1,也就禁止IRQ和FIQ中断,哪后的10011,就是管理模式了.
(2)watch dog中断及分频的设置
058 # if defined(CONFIG_S3C2400)
059 # define pWTCON 0x15300000
060 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */
061 # define CLKDIVN 0x14800014 /* clock divisor register */
062 #else
063 # define pWTCON 0x53000000
064 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
065 # define INTSUBMSK 0x4A00001C
066 # define CLKDIVN 0x4C000014 /* clock divisor register */
067 # endif
069 ldr r0, =pWTCON
070 mov r1, #0x0
071 str r1, [r0]
072 /*
073 * mask all IRQs by setting all bits in the INTMR - default
074 */
075 movr1, #0xffffffff
076 ldr r0, =INTMSK
077 str r1, [r0]
084 #if defined(CONFIG_S3C2440)
085 ldr r1, =0x7fff
086 ldr r0, =INTSUBMSK
087 str r1, [r0]
088 #endif
089 #if defined(CONFIG_S3C2440)
090 #define MPLLCON 0x4C000004
091 #define UPLLCON 0x4C000008
092 ldr r0, =CLKDIVN
093 movr1, #5
094 str r1,[r0]
095
096 mrc p15,0,r1,c1,c0,0
097 orr r1,r1,#0xc0000000
098 mcrp15,0,r1,c1,c0,0
100 ldr r0, =MPLLCON
101 ldr r1, =0x7F021
102 str r1,[r0]
104 ldr r0,=UPLLCON
105 ldr r1,=0x38022
106 str r1,[r0]
107 #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
第63-67行分别用宏定义了watchdog intmask等寄存器的地址,查datasheet看寄存器的地址.
第69-71行把watchdog的寄存器设为0,关闭watchdog。
第75-77行是设置INTMSK寄存器,屏蔽掉所有中断,设为0xffffffff。
第85-87行也是屏蔽中断。
第90-91行分别对MPLL和UPLL的宏定义,MPLL用于定义FCLK,HCLK,PCLK,FCLK用于CPU核,HCLK用于AHB总线上的设备.比如,中断控制器,LCD控制器,DMA等.PCLK用于APB总线上的设备,如WATCHDOG,,IIS,I2C,PWM定时器等.UPLL主要USB设备
第92-94行设置CLKDIVN寄存器,这个寄存器用于设置FCLK HCLK PCLK三者的比例,这里CLKDIVN的值为5,根据samsung手册,5为101,10时HCLK=FCLK/4,最后一位的1,PCLK=HCLK/2,所以FCLK:HCLK:PCLK=1:4:8了.
第96-98行这段代码没有研究,参考了别人的,对s3c2440,如果HDIVN非0,则由fast bus mode变为asynchronous bus mode.
第100-102行是对MPLL寄存器的设置,这里面设置0x7F021。
第104-106行对UPLL寄存器设置