uboot1.1.6中启动流程
U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:
(1)第一阶段的功能
Ø 硬件设备初始化
Ø 加载U-Boot第二阶段代码到RAM空间
Ø 设置好栈
Ø 跳转到第二阶段代码入口
(2)第二阶段的功能
Ø 初始化本阶段使用的硬件设备
Ø 检测系统内存映射
Ø 将内核从Flash读取到RAM中
Ø 为内核设置启动参数
Ø 调用内核
图 2.1 U-Boot启动第一阶段流程
Linux环境下的源码一般的编译起始文件是根目录下的Makefile文件,我们先充这个地方说起,看看能不能通过这个文件一直找到ARM的启动流程。
这里是整个Makefile的开始地方。既然系统是需要通过Makefile编译并链接成镜像文件的,那么
这个文件中一定在这一行的存在一个链接脚本。
这里有删除u-boot.lds的操作,什么这是链接镜像的脚本。再去单独查找整个文件,发现有很多
因为这里我们是要查看ok6410的处理器,所以,我们选中了
U-boot.lds (board\samsung\smdk6410)16192012/1/17
OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.=0x00000000;
.= ALIGN(4);
.text :
{
cpu/s3c64xx/start.o (.text)
cpu/s3c64xx/s3c6410/cpu_init.o (.text)
cpu/s3c64xx/onenand_cp.o (.text)
cpu/s3c64xx/nand_cp.o (.text)
cpu/s3c64xx/movi.o (.text)
*(.text)
lib_arm/div0.o
}
.= ALIGN(4);
.rodata :{*(.rodata)}
.= ALIGN(4);
.data :{*(.data)}
.= ALIGN(4);
.got :{*(.got)}
__u_boot_cmd_start =.;
.u_boot_cmd :{*(.u_boot_cmd)}
__u_boot_cmd_end =.;
.= ALIGN(4);
.mmudata :{*(.mmudata)}
.= ALIGN(4);
__bss_start =.;
.bss :{*(.bss)}
_end =.;
}
这里的代码很简单,就是主入口函数_start,并且执行下述几个文件
cpu/s3c64xx/start.o (.text)
cpu/s3c64xx/s3c6410/cpu_init.o (.text)
cpu/s3c64xx/onenand_cp.o (.text)
cpu/s3c64xx/nand_cp.o (.text)
cpu/s3c64xx/movi.o (.text)
经查找,入口函数_start在start.S (cpu\s3c64xx)中,
这个时候,我们就可以看到ok6410是怎么启动的了,也就是它的启动流程正式开始执行了。先执行reset函数:
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f /* 1f=1 1111 = ~ = 0 0000 */
orr r0,r0,#0xd3 /* d3=1101 0011 => 1 0011*/
msr cpsr,r0
31 | 30 | 29 | 28 | 27 | ~ | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||
N | Z | C | V | 保留 | I | F | T | M4 | M3 | M2 | M1 | M0 | |||||
N | Negative/Less Than | I | IRQ disable | ||||||||||||||
Z | Zero | F | FIQ disable | ||||||||||||||
C | Carry/Borrow/Extend | T | State bit | ||||||||||||||
V | Overflow | M0~4 | Mode bits |
1、条件码标志
N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行。条件码标志各位的具体含义如下表所示:
标志位 | 含 义 |
N | 当用两个补码表示的带符号数进行运算时,N=1表示运算的结果为负数;N=0表示运算的结果为正数或零 |
Z | Z=1表示运算的结果为零,Z=0表示运算的结果非零。 |
C | 可以有4种方法设置C的值: |
-加法运算(包括CMP):当运算结果产生了进位时(无符号数溢出),C=1,否则C=0。 | |
-减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1。 | |
-对于包含移位操作的非加/减运算指令,C为移出值的最后一位。 | |
-对于其它的非加/减运算指令,C的值通常不会改变。 | |
V | 可以有2种方法设置V的值: |
-对于加减法运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号位溢出 | |
-对于其它的非加/减运算指令,V的值通常不会改变。 | |
Q | 在ARM V5及以上版本的E系列处理器中,用Q标志位指示增强的DSP运算指令是否发生了溢出。在其它版本的处理器中,Q标志位无定义 |
在ARM状态下,绝大多数的指令都是有条件执行的;在THUMB状态下,仅有分支指令是条件执行的。
2 控制位
CPSR的低8位(包括I、F、T和M[4:0])称为控制位,当发生异常时这些位可以被改变。如果处理器运行于特权模式时,这些位也可以由程序修改。
·中断禁止位I、F:置1时,禁止IRQ中断和FIQ中断。
·T标志位:该位反映处理器的运行状态。当该位为1时,程序运行于THUMB状态,否则运行于ARM状态。该信号反映在外部引脚TBIT上。在程序中不得修改CPSR中的TBIT位,否则处理器工作状态不能确定。
·运行模式位M[4:0]:这几位是模式位,这些位决定了处理器的运行模式。具体含义如下表所示:
·保留位:CPSR中的其余位为保留位,当改变CPSR中的条件码标志位或者控制位时,保留位不要改变,在程序中也不要用保留位存储数据。保留位将用于ARM版本的扩展。
M[4:0] | 处理器模式 | ARM模式可访问的寄存器 | THUMB模式可访问的寄存器 |
0b10000 | 用户模式 usr | PC,CPSR,R0~R14 | PC,CPSR,R0~R7,LR,SP |
0b10001 | FIQ模式 fiq | PC,CPSR,SPSR_fiq,R14_fiq~R8_fiq,R0~R7 | PC,CPSR,SPSR_fiq,LR_fiq,SP_fiq,R0~R7 |
0b10010 | IRQ模式 irq | PC,CPSR,SPSR_irq,R14_irq~R13_irq,R0~R12 | PC,CPSR,SPSR_irq,LR_irq,SP_irq,R0~R7 |
0b10011 | 管理模式 svc | PC,CPSR,SPSR_svc,R14_svc~R13_svc,R0~R12 | PC,CPSR,SPSR_svc,LR_svc,SP_svc,R0~R7 |
0b10111 | 中止模式 abt | PC,CPSR,SPSR_abt,R14_abt~R13_abt,R0~R12 | PC,CPSR,SPSR_abt,LR_abt,SP_abt,R0~R7 |
0b11011 | 未定义模式 und | PC,CPSR,SPSR_und,R14_und~R13_und,R0~R12 | PC,CPSR,SPSR_und,LR_und,SP_und,R0~R7 |
0b11111 | 系统模式 sys | PC,CPSR,R0~R14 | PC,CPSR,LR,SP,R0~R74 |
执行完reset后,顺序接着执行cpu_init_crit
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0,#0
mcr p15,0, r0, c7, c7,0/* flush v3/v4 cache */
mcr p15,0, r0, c8, c7,0/* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
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)
orr r0, r0,#0x00000002 @ set bit 2 (A) Align
orr r0, r0,#0x00001000 @ set bit 12 (I) I-Cache
mcr p15,0, r0, c1, c0,0
/* Peri port setup */
ldr r0,=0x70000000
orr r0, r0,#0x13
mcr p15,0,r0,c15,c2,4@256M(0x70000000-0x7fffffff)
接着顺序执行lowlevel_init函数
lowlevel_init.S (board\samsung\smdk6410)86152012/3/31
#include<config.h>
#include<version.h>
#include<s3c6410.h>
#include"smdk6410_val.h"
_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
- ...
- /* LED on only #8 */
- ...
- /* Disable Watchdog */
- ...
/* init system clock */
- bl system_clock_init
- ...
点led灯
执行关闭看门狗
初始化系统时钟
lowlevel_init.S (board\samsung\smdk6410) 8615 2012/3/31
/*
* system_clock_init: Initialize core clock and bus clock.
* void system_clock_init(void)
*/
system_clock_init:
ldr r0,=ELFIN_CLOCK_POWER_BASE @0x7e00f000
ldr r1,[r0,#OTHERS_OFFSET]
mov r2,#0x40
orr r1, r1, r2
str r1,[r0,#OTHERS_OFFSET]
nop
nop
nop
nop
nop
ldr r2,=0x80
orr r1, r1, r2
str r1,[r0,#OTHERS_OFFSET]
check_syncack:
ldr r1,[r0,#OTHERS_OFFSET]
ldr r2,=0xf00
and r1, r1, r2
cmp r1,#0xf00
bne check_syncack
mov r1,#0xff00
orr r1, r1,#0xff
str r1,[r0,#APLL_LOCK_OFFSET]
str r1,[r0,#MPLL_LOCK_OFFSET]
str r1,[r0,#EPLL_LOCK_OFFSET]
/* CLKUART(=66.5Mhz) = CLKUART_input(532/2=266Mhz) / (UART_RATIO(3)+1) */
/* CLKUART(=50Mhz) = CLKUART_input(400/2=200Mhz) / (UART_RATIO(3)+1) */
/* Now, When you use UART CLK SRC by EXT_UCLK1, We support 532MHz & 400MHz value */
#if defined(CONFIG_CLKSRC_CLKUART)
ldr r1,[r0,#CLK_DIV2_OFFSET]
bic r1, r1,#0x70000
orr r1, r1,#0x30000
str r1,[r0,#CLK_DIV2_OFFSET]
#endif
- ldr r1,[r0,#CLK_DIV0_OFFSET] /*Set Clock Divider*/
bic r1, r1,#0x30000
bic r1, r1,#0xff00
bic r1, r1,#0xff
ldr r2,=CLK_DIV_VAL
orr r1, r1, r2
str r1,[r0,#CLK_DIV0_OFFSET]
ldr r1,=APLL_VAL
str r1,[r0,#APLL_CON_OFFSET]
ldr r1,=MPLL_VAL
str r1,[r0,#MPLL_CON_OFFSET]
ldr r1,=0x80200203/* FOUT of EPLL is 96MHz */
str r1,[r0,#EPLL_CON0_OFFSET]
ldr r1,=0x0
str r1,[r0,#EPLL_CON1_OFFSET]
ldr r1,[r0,#CLK_SRC_OFFSET] /* APLL, MPLL, EPLL select to Fout */
ldr r2,=0x2007
orr r1, r1, r2
str r1,[r0,#CLK_SRC_OFFSET]
/* wait at least 200us to stablize all clock */
mov r1,#0x10000
1: subs r1, r1,#1
bne 1b
ldr r1,[r0,#OTHERS_OFFSET]
orr r1, r1,#0x20
str r1,[r0,#OTHERS_OFFSET]
mov pc, lr /* 子函数执行完毕返回 */
执行完后顺序执行
/* for UART */
bl uart_asm_init
具体函数如下:
/*
* uart_asm_init: Initialize UART in asm mode, 115200bps fixed.
* void uart_asm_init(void)
*/
uart_asm_init:
/* set GPIO to enable UART */
@ GPIO setting for UART
ldr r0,=ELFIN_GPIO_BASE
ldr r1,=0x220022
str r1,[r0,#GPACON_OFFSET]
ldr r1,=0x2222
str r1,[r0,#GPBCON_OFFSET]
ldr r0,=ELFIN_UART_CONSOLE_BASE @0x7F005000
mov r1,#0x0
str r1,[r0,#UFCON_OFFSET]
str r1,[r0,#UMCON_OFFSET]
mov r1,#0x3 @was 0.
str r1,[r0,#ULCON_OFFSET]
#if defined(CONFIG_CLKSRC_CLKUART)
ldr r1,=0xe45/* UARTCLK SRC = 11 => EXT_UCLK1*/
#else
ldr r1,=0x245/* UARTCLK SRC = x0 => PCLK */
#endif
str r1,[r0,#UCON_OFFSET]
#if defined(CONFIG_UART_50)
ldr r1,=0x1A
#elif defined(CONFIG_UART_66)
ldr r1,=0x22
#else
ldr r1,=0x1A
#endif
str r1,[r0,#UBRDIV_OFFSET]
#if defined(CONFIG_UART_50)
ldr r1,=0x3
#elif defined(CONFIG_UART_66)
ldr r1,=0x1FFF
#else
ldr r1,=0x3
#endif
str r1,[r0,#UDIVSLOT_OFFSET]
ldr r1,=0x4f4f4f4f
str r1,[r0,#UTXH_OFFSET] @'O'
mov pc, lr
在接着顺序执行
ldr r0,=ELFIN_UART_BASE
ldr r1,=0x4b4b4b4b
str r1,[r0,#UTXH_OFFSET]
#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
#endif
bl mem_ctrl_asm_init
#if 1
ldr r0,=(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1,[r0]
bic r1, r1,#0xfffffff7
cmp r1,#0x8
beq wakeup_reset
#endif
初始化NAND
/*
* Nand Interface Init for SMDK6400 */
nand_asm_init:
ldr r0,=ELFIN_NAND_BASE
ldr r1,[r0,#NFCONF_OFFSET]
orr r1, r1,#0x70
orr r1, r1,#0x7700
str r1,[r0,#NFCONF_OFFSET]
ldr r1,[r0,#NFCONT_OFFSET]
orr r1, r1,#0x03
str r1,[r0,#NFCONT_OFFSET]
mov pc, lr
内存控制器初始化
cpu_init.S (cpu\s3c64xx\s3c6410)32702012/1/17
#include<config.h>
#include<s3c6410.h>
.globl mem_ctrl_asm_init
mem_ctrl_asm_init:
ldr r0,=ELFIN_MEM_SYS_CFG @Memory sussystem address 0x7e00f120
mov r1,#0xd @ Xm0CSn2 = NFCON CS0, Xm0CSn3 = NFCON CS1
str r1,[r0]
...
mov pc, lr
最后执行
wakeup_reset:
/*Clear wakeup status register*/
ldr r0,=(ELFIN_CLOCK_POWER_BASE+WAKEUP_STAT_OFFSET)
ldr r1,[r0]
str r1,[r0]
/*LED test*/
ldr r0,=ELFIN_GPIO_BASE
ldr r1,=0x3000
str r1,[r0,#GPNDAT_OFFSET]
/*Load return address and jump to kernel*/
ldr r0,=(ELFIN_CLOCK_POWER_BASE+INF_REG0_OFFSET)
ldr r1,[r0]/* r1 = physical address of s3c6400_cpu_resume function*/
mov pc, r1 /*Jump to kernel (sleep-s3c6400.S)*/
最后回到start.S (cpu\s3c64xx)162102016/9/4
/* 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 after_copy /* r0 == r1 then skip flash copy */
接着执行
after_copy:
#ifdef CONFIG_ENABLE_MMU
enable_mmu:
/* enable domain access */
ldr r5,=0x0000ffff
mcr p15,0, r5, c3, c0,0@ load domain access register
/* Set the TTB register */
ldr r0,
ldr r1,=CFG_PHY_UBOOT_BASE
ldr r2,=0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15,0, r1, c2, c0,0
/* Enable the MMU */
mmu_on:
mrc p15,0, r0, c1, c0,0
orr r0, r0,#1 /* Set CR_M to enable MMU */
mcr p15,0, r0, c1, c0,0
nop
nop
nop
nop
#endif
skip_hw_init:
/* Set up the stack */
stack_setup:
#ifdef CONFIG_MEMORY_UPPER_CODE
ldr sp,=(CFG_UBOOT_BASE + CFG_UBOOT_SIZE -0xc)
#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 */
#ifdef CONFIG_USE_IRQ
sub r0, r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0,#12 /* 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 */
clbss_l:
str r2,[r0]/* clear loop... */
add r0, r0,#4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
- _start_armboot:
.word start_armboot
到执行start_armboot函数这一步,就开始跳出硬件依赖了,跳转到第二阶段代码入口
Board.c (lib_arm)142052012/7/11
图 2.3 U-Boot第二阶段执行流程
通过bootm引导linux内核启动
使用do_bootm_linux引导kernel
直至
至此,Linux系统开始运行。