u-boot之源码分析阶段一
--Edison Gao
//u-boot.1.1.6
//u-boot1.1.6/cpu/arm920t/start.S
u-boot顶层目录有很多子目录,下面介绍一些主要的目录
(1)arch:对应不同架构的cpu,子目录的名字就是所支持的cpu框架的名称,如arch目录下含有arm,arv32以及x86等,这些目录可以继续细分。例如arm下含有cpu,include和lib等目录,其中cpu用于区分不同体系的arm内核
(2)common:该目录存在的是一些公共的通用代码文件,包括用于实现各种公共命令的cmd_*.c,env_*.c文件以及一些通用函数。它们独立于处理器的体系架构,直接跟客户打交道,是用户与设备驱动之间沟通的纽带,也是U-Boot的精髓所在
(3)drivers:该目录存放的是各类外设的驱动程序,如mmc,serial,和net等。
(4)fs:支持文件系统
(5)net:该目录里面的文件实现了各种网络协议
(6)nand_apl:实现U-Boot从Nand Flash中启动的设备驱动。
(7)include:一些头文件和单板配置文件,所有单板配置的文件位于include/configs目录下。
(8)tools:常用工具,包括用于制作uImage和mkimage工具。
(9)Makefile:控制整个工程的编译
一:设置ARM工作模式
那么我们首先分析u-boot的启动过程(Start.S),一般都是以汇编语言编写
b:跳转指令
bl:带返回的跳转指令
bx:带状态切换的跳转指令
blx:带返回和状态切换的跳转指令
.globl _start
_start: b reset //跳转到reset
ldr pc, _undefined_instruction //未定义指令异常
ldr pc, _software_interrupt //软中断,一般用于Linux系统的系统调用,就是从用户模式进入保护模式 后者进入超级用户模式
ldr pc, _prefetch_abort //预取止终止 CPU取不到下一条指令
ldr pc, _data_abort //cpu取不到值
ldr pc, _not_used
ldr pc, _irq //一般中断
ldr pc, _fiq //快中断 u-boot中遇不到 ,我们可以不需要分析
_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
跳转到reset函数,这里对reset分析
reset:
/*
* set the cpu to SVC32 mode 设置cpu到svc32模式 即管理员模式
*/
mrs r0,cpsr //读状态寄存器指令,将寄存器cpsr读取到r0中
bic r0,r0,#0x1f //位清除指令 清除r0寄存器的低6位 ,将r0寄存器的低6位置0
orr r0,r0,#0xd3 //将r0寄存器与0x11010011 或运算
msr cpsr,r0 //写状态寄存器指令,即cpsr = 0x11010011
/* cpsr的低八位为0x11010011
第7位,即为I位,当i=1时禁止IRQ中断
第6位,即为F位,当F=1时禁止FIQ中断
第5位,即为T位,当T=1时执行ARM指令;T=0时执行Thumb指令
低5位,即为0x13,为管理员模式*/
这样我们就把arm设置为管理模式(svc)。
二:关闭看门狗
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) //关闭看门狗
# define pWTCON 0x53000000
# define INTMOD 0X4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
ldr r0, =pWTCON // 将pWTCON地址放入r0寄存器中
mov r1, #0x0
str r1, [r0] //将r1寄存器的值放入r0地址中
三:屏蔽所有中断
mov r1, #0xffffffff //屏蔽所有中断 r1 = 0xffffffff,
ldr r0, =INTMSK //将地址INTMSK值加载入r0寄存器中
str r1, [r0] //将r1的值放入r0寄存器中 即INTMSK=0xffffffff
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0] //类似 地址INTSUBMSK = 0x3ff
四:时钟初始化
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0] // CLKDIVN = 0x2 分频 FCLK:HCLK:PCLK = 1:2:4
五:CPU初始化
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
adr r0, _start /* r0 <- current position of code *///读地址指令 当前地址?
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug判断两个地址是否相等证明sdram又没被初始化*/
blne cpu_init_crit //bl是跳转命令,ne是不相等执行,这句的意思的cspr寄存器的Z标志位为1时执行跳转
cpu_init_crit:
/*
* flush v4 I/D caches cashes初始化
*/
mov r0, #0 //将0这个立即数加载到r0寄存器中
mcr p15, 0, r0, c7, c7, 0//r0是ARM处理器的寄存器,c7是协处理器的寄存器,第一个0是操作码1,第二个0时操作码2,此处是将c7写入0,使ICache与DCache失效/* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0//将c8写入0,使TLB失效 /* flush v4 TLB */
/*
* disable MMU stuff and caches 关闭mmu和cashes
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS) //
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM) //CPU为小字节序,关闭Dcaches,数据访问时不进行地址对齐检查,关闭mmu
orr r0, r0, #0x00000002 @ set bit 2 (A) Align //数据访问时进行地址对齐检查
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache //开启Icaches
mcr p15, 0, r0, c1, c0, 0 //将c1写入0 ,,关闭mmu,caches
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr
五:设置栈
stack_setup:
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 */
六:将代码从flash读到SDRAM中
我们可以了解,对于2440
上电后,2440的iROM的代码会先运行 即干了两件事
a.初始化时钟,关闭看门狗之类
b.将flash的头4k内存加载到片内内存中,这4k代码主要是配置好sdram,然后bootloder的大部分再到sdram中
relocate: /* relocate U-Boot to RAM */ //把代码从flash读到sdram里面去
adr r0, _start /* r0 <- current position of code */ //读地址指令 当前地址?
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug 判断两个地址是否相等*/
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
#else
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
七:清bss段
clear_bss:
ldr r0, _bss_start /* find start of bss segment */ //清BSS段 全部清0
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
最后的代码是一些异常处理函数。这里就不一 一探讨了
下一节我们会继续分析源码第二阶段