uboot分析 (2009-02-06 15:37)

来源:互联网 发布:怎么注册阿里云邮箱 编辑:程序博客网 时间:2024/05/16 06:02
 
uboot分析 (2009-02-06 15:37)
分类: armlinux

大多数bootloader都分为stage1stage2两部分,u-boot也不例外。依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
1
Stage1 start.S代码结构
u-boot
stage1代码通常放在start.S文件中,他用汇编语言写成,其主要代码部分如下:
1)定义入口。由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROMFlash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
2)设置异常向量(Exception Vector)。
3)设置CPU的速度、时钟频率及终端控制寄存器。
4)初始化内存控制器。
5)将ROM中的程序复制到RAM中。
6)初始化堆栈。
7)转到RAM中执行,该工作可使用指令ldr pc来完成。
2
Stage2 C语言代码部分
lib_arm/board.c
中的start arm bootC语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-bootarmboot)的主函数,该函数只要完成如下操作:
1)调用一系列的初始化函数。
2)初始化Flash设备。
3)初始化系统内存分配函数。
4)如果目标系统拥有NAND设备,则初始化NAND设备。
5)如果目标系统有显示设备,则初始化该类设备。
6)初始化相关网络设备,填写IPMAC地址等。
7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。
3
U-Boot的启动顺序(示例,其他u-boot版本类似)
cpu/arm920t/start.S
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/

;定义变量_start,然后跳转到处理器复位代码
.globl _start //u-boot
启动入口
_start: b reset
;
产生中断则利用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 //
快速中断向量


;
利用.word来在当前位置放置一个值,这个值实际上就用对应的中断处理函数的地址
;.word
的意义为在当前地址处放入一个16bits
_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


/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
;
定义变量
_TEXT_BASE:
.word TEXT_BASE

.globl _armboot_start
_armboot_start:
.word _start

/*
* These are defined in the board-specific linker script.
*/
.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


/*
* the actual reset code
*/
;
实际处理代码
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
;bic
清除指定为1的位
bic r0,r0,#0x1f
;orr
逻辑或操作
orr r0,r0,#0xd3
;
经过以上两步r0值控制位位11010011,第04位标识处理器当前所处模式为10011(32位管理模式),第67
;
1标识禁止IRQFIQ中断,第5位为0标识程序运行为arm状态,若其为1则运行在thumb状态
;
设置处理器为32位管理模式,并运行与arm状态
msr cpsr,r0

/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
;
看门狗寄存器地址
# define pWTCON 0x53000000
;
中断掩码寄存器,决定那个中断源被屏蔽,某位为1则屏蔽中断源,初始值为0xFFFFFFFF,屏蔽所有中断
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
;
中断子掩码寄存器,该寄存器只能屏蔽11个中断源,因此其仅低11位有效,初始值为0x7FF
# define INTSUBMSK 0x4A00001C
;
时钟分频控制寄存器
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
;
将看门狗寄存器清空,其各位含义为,第0位为1则当看门狗定时器溢出时重启,为0则不重启,初值为1
;
2位为中断使能位,初值为0
;
34位为时钟分频因子,初值为00
;
5位为看门狗的使能位初值为1
;
815位为比例因子,初值为0x80
ldr r0, =pWTCON
mov r1, #0x0
;
将看门狗寄存器所有位置零,关闭看门狗,其实只要将第5位置0即可
str r1, [r0]
;
屏蔽所有中断,实际上中断屏蔽掩码寄存器初值即为0xFFFFFFF
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
;
设置子中断掩码寄存器
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
;
设置时钟寄存器,CLKDIVN0位为PDIVN,为0PCLK=HCLK,为1PCLK=HCLK/2
;
1位为HDIVN,为0HCLK=FCLK,1HCLK=FCLK/2
;
这里两位均为1,则FCLK:HCLK:PCLK = 4:2:1
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */

/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
;
对临界寄存器的初始化,如果从ram中启动则不执行,如果重启则执行
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
;
重定向代码,也就是从flash中复制到ram
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
;
当前代码地址,adr获取当前代码的地址信息,若从ram运行则_start=TEXT_BASE,否则_start=0x00000000
adr r0, _start /* r0 <- current position of code */
;
获取_TEXT_BASE
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
;
两者相等,表示从ram运行则跳转到堆栈设置
beq stack_setup
;
不相等则表示从flash中运行,重定向代码
ldr r2, _armboot_start
;
获取未初始化数据段地址
ldr r3, _bss_start
;
计算代码段大小
sub r2, r3, r2 /* r2 <- size of armboot */
;
计算代码段终止地址
add r2, r0, r2 /* r2 <- source end address */
;
复制代码,r0为代码的起始地址,r1ram中地址,r2为代码的终止地址
;
每次copy后将r0值递增同r2比较来判断是否复制完成
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
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

/* Set up the stack */
stack_setup:
;
获取_TEXT_BASE
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
;
获取分配区域起始指针,CFG_MALLOC_LEN=128*1024+CFG_ENV_SIZE=128*1024+0x10000=192K
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
;
另外分配128bytes来存储开发板信息
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
;
再减去12bytes用于栈起点
sub sp, r0, #12 /* leave 3 words for abort-stack */
;
清空未初始化数据段
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

#if 0
;
关闭看门狗
/* try doing this stuff after the relocation */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]

/*
* mask all IRQs by setting all bits in the INTMR - default
*/
;
禁止中断
mov r1, #0xffffffff
ldr r0, =INTMR
str r1, [r0]
;
设置时钟
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/* END stuff after relocation */
#endif
;
完成复制后跳转到start_armboot,到这里就进入函数lib_arm/board.cstart_armboot函数中
ldr pc, _start_armboot

_start_armboot: .word start_armboot
;
这里指的从flash中运行是指的从flash rom中运行,也就是常说的从nor flash中运行程序,现在有很多开发板;都是利用SDRAMNAND-FLASH共同工作,所以需要添加从nand flash启动的代码。在ldr pc,_start_armboot;;添加如下代码:
#ifdef CONFIG_S3C2410_NAND_BOOT
bl copy_myself

@ jump to ram
ldr r1, =on_the_ram
add pc, r1, #0
nop
nop
1: b 1b @ infinite loop

on_the_ram:
#endif

;开始就跳转到函数copy_myself中,
#ifdef CONFIG_S3C2410_NAND_BOOT
copy_myself:
;
保存断点地址
mov r10, lr
@ reset NAND
;NAND_CTRL_BASE
nand flash的寄存器基址为0x4E000000
;
根据flash手册来复位flash
mov r1, #NAND_CTL_BASE
;
通过nand flash配置寄存器配置nand flash,该寄存器仅低16位有效
;
15位为nand flash控制器使能位,第12位为初始化ECC使能位,第11位为nand falsh memory nFCE使能
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
;
将第11位清零,使能芯片
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
;
写入命令
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
;
循环延时
mov r3, #0 @ wait
1: add r3, r3, #0x1
cmp r3, #0xa
blt 1b
;
利用状态寄存器测试flash内部操作是否完成,如果完成则其状态寄存器将返回1
;
等待flash操作完成
2: ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq 2b
;
禁止芯片
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]

@ get read to call C functions (for nand_read())
;
建立堆栈,栈起点为0x33f00000,大小为0x8000
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0

@ copy U-BOOT to RAM
;UBOOT_RAM_BASE
应该同TEXT_BASE相同
ldr r0, =UBOOT_RAM_BASE
mov r1, #0x0 @address
mov r2, #0x30000 @size
;
这里设置大小为192K,为什么取这个值?为什么这三个寄存器的值就作为参数传递进去了?
;
跳转到board/smdk2410/nand_read.c中的nand_read_ll函数,代码见后面
;
该函数需要三个参数,r0为其在ram中的起始地址,r1为在源地址也就是flash中的起始地址,r2为需要
;
复制的中大小
bl nand_read_ll

tst r0, #0x0 ;r0
为返回值
;
如果成功完成复制则跳转到ok_nand_read
beq ok_nand_read

#ifdef CONFIG_DEBUG_LL
bad_nand_read:
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
1: b 1b @ infinite loop
#endif
;
打印信息
ok_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
;
校验,因为从nand flash启动只需要将bootloader的前4K代码复制到stepping stone处即可
@ verify
mov r0, #0
ldr r1, =UBOOT_RAM_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq done_nand_read
bne go_next

notmatch:
#ifdef CONFIG_DEBUG_LL
sub r0, r0, #4
ldr r1, SerBase
bl PrintHexWord
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
#endif
1: b 1b

done_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
;
恢复断点,程序继续运行
mov pc, r10

;内存清零???
@ clear memory
@ r0: start address
@ r1: length
mem_clear:
mov r2, #0
mov r3, r2
mov r4, r2
mov r5, r2
mov r6, r2
mov r7, r2
mov r8, r2
mov r9, r2
clear_loop:
stmia r0!, {r2-r9}
subs r1, r1, #(8 * 4)
bne clear_loop
mov pc, lr

#endif @ CONFIG_S3C2410_NAND_BOOT


/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/

;对临界寄存器的初始化
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
*/
;
清空指令和数据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

/*
* 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.
*/
;
在重定向代码之前,必须初始化内存时序,因为重定向时需要将flash中的代码复制到内存中
;
内存初始化的代码在开发板目录下
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/
;
中断处理
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72

#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52

#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0

#define MODE_SVC 0x13
#define I_BIT 0x80

/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/

.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC

add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm

.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm

.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm

.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack

str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]

mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm

.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm

.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm

/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction

.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt

.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort

.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort

.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used

#ifdef CONFIG_USE_IRQ

.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs

.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs

#else

.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq

.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq

#endif
#ifdef CONFIG_S3C2410_NAND_BOOT
.align 2
DW_STACK_START:
.word STACK_BASE+STACK_SIZE-4
#endif

//该函数来自于board/smdk2410/nand_read.c
//
通过r0r1r2三个寄存器传递参数过来,返回值为r0
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;

if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
return -1; /* invalid alignment */
}
//
按照芯片的时序来进行读写操作
/* chip Enable */
NFCONF &= ~0x800;
for(i=0; i<10; i++);

for(i=start_addr; i < (start_addr + size);) {
/* READ0 */
NFCMD = 0;

/* Write Address */
NFADDR = i & 0xff;
NFADDR = (i >> 9) & 0xff;
NFADDR = (i >> 17) & 0xff;
NFADDR = (i >> 25) & 0xff;

wait_idle();

for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
*buf = (NFDATA & 0xff);
buf++;
}
}

/* chip Disable */
NFCONF |= 0x800; /* chip disable */

return 0;
}

board/qt2410/lowlevel_init.S
;
总线宽度和等待寄存器
#define BWSCON 0x48000000
;
宽度8bits16bits32bits
/* BWSCON */
#define DW8 (0x0)
#define DW16 (0x1)
#define DW32 (0x2)
;
等待
#define WAIT (0x1<<2)
;UBLB
标识引脚信号的类型,为0则为nWBE,为1则为nBE
#define UBLB (0x1<<3)
;
定义总线类型
#define B1_BWSCON (DW32)
#define B2_BWSCON (DW16)
#define B3_BWSCON (DW16 + WAIT + UBLB)
#define B4_BWSCON (DW16)1
#define B5_BWSCON (DW16)
#define B6_BWSCON (DW32)
#define B7_BWSCON (DW32)
;bank0
寄存器
/* BANK0CON */
#define B0_Tacs 0x0 /* 0clk */
#define B0_Tcos 0x0 /* 0clk */
#define B0_Tacc 0x7 /* 14clk */
#define B0_Tcoh 0x0 /* 0clk */
#define B0_Tah 0x0 /* 0clk */
#define B0_Tacp 0x0
#define B0_PMC 0x0 /* normal */
;bank1
寄存器
/* BANK1CON */
#define B1_Tacs 0x0 /* 0clk */
#define B1_Tcos 0x0 /* 0clk */
#define B1_Tacc 0x7 /* 14clk */
#define B1_Tcoh 0x0 /* 0clk */
#define B1_Tah 0x0 /* 0clk */
#define B1_Tacp 0x0
#define B1_PMC 0x0
;bank2
寄存器
#define B2_Tacs 0x0
#define B2_Tcos 0x0
#define B2_Tacc 0x7
#define B2_Tcoh 0x0
#define B2_Tah 0x0
#define B2_Tacp 0x0
#define B2_PMC 0x0
;bank3
寄存器
#define B3_Tacs 0x0 /* 0clk */
#define B3_Tcos 0x3 /* 4clk */
#define B3_Tacc 0x7 /* 14clk */
#define B3_Tcoh 0x1 /* 1clk */
#define B3_Tah 0x0 /* 0clk */
#define B3_Tacp 0x3 /* 6clk */
#define B3_PMC 0x0 /* normal */
;bank4
寄存器
#define B4_Tacs 0x0 /* 0clk */
#define B4_Tcos 0x0 /* 0clk */
#define B4_Tacc 0x7 /* 14clk */
#define B4_Tcoh 0x0 /* 0clk */
#define B4_Tah 0x0 /* 0clk */
#define B4_Tacp 0x0
#define B4_PMC 0x0 /* normal */
;bank5
寄存器
#define B5_Tacs 0x0 /* 0clk */
#define B5_Tcos 0x0 /* 0clk */
#define B5_Tacc 0x7 /* 14clk */
#define B5_Tcoh 0x0 /* 0clk */
#define B5_Tah 0x0 /* 0clk */
#define B5_Tacp 0x0
#define B5_PMC 0x0 /* normal */
;bank6
寄存器
#define B6_MT 0x3 /* SDRAM */
#define B6_Trcd 0x1
#define B6_SCAN 0x1 /* 9bit */
;bank7
寄存器
#define B7_MT 0x3 /* SDRAM */
#define B7_Trcd 0x1 /* 3clk */
#define B7_SCAN 0x1 /* 9bit */

/* REFRESH parameter */
#define REFEN 0x1 /* Refresh enable */
#define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */
#define Trp 0x0 /* 2clk */
#define Trc 0x3 /* 7clk */
#define Tchr 0x2 /* 3clk */
#define REFCNT 1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
/**************************************/

_TEXT_BASE:
.word TEXT_BASE

.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
/*added by kyle*/
lmov r3,pc
lldr r4,=0x3FFF0000
land r3,r3,r4
laad r0,r0,r3
ladd r2,r2,r3
0:

ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b

/* everything is fine now */
mov pc, lr

.ltorg
/* the literal pools origin */

SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0x32
.word 0x30
.word 0x30
;
定义函数指针
typedef int (init_fnc_t) (void);
;
定义函数指针数组,对硬件初始化按照该数组进行
init_fnc_t *init_sequence[] = {
cpu_init, //cpu/arm920t/cpu.c
中定义,该函数为空,因为没有采用IRQFIQ模式
board_init, //board/smdk2410/smdk2410.c
interrupt_init, //cpu/arm920t/s3c24x0/interrupt.c
env_init, //tools/env/FW_env.c
init_baudrate, //lib_arm/board.c
serial_init, //cpu/arm920t/s3c24x0/serial.c
console_init_f, //common/console.c
display_banner, //lib_arm/board.c
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, //
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, //
#endif
dram_init, //board/smdk2410/smdk2410.c
display_dram_config,//lib_arm/board.c
NULL,
};



int board_init (void)
{
;
将时间相关的寄存器定义为结构体S3C24X0_CLOCK_POWERS3C24X0_GPIO也是一样
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
;
设置cpu时钟
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;

/* configure MPLL */
//M_MDIV=0xA1,M_PDIV=0x3,M_SDIV=0x1
//
这样系统时钟为202.80M
clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);

/* some delay between MPLL and UPLL */
delay (4000);
;USB
时钟为48M
/* configure UPLL */
clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);

/* some delay between MPLL and UPLL */
delay (8000);
;
设置GPIO
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
gpio->GPBCON = 0x00044555;
gpio->GPBUP = 0x000007FF;
gpio->GPCCON = 0xAAAAAAAA;
gpio->GPCUP = 0x0000FFFF;
gpio->GPDCON = 0xAAAAAAAA;
gpio->GPDUP = 0x0000FFFF;
gpio->GPECON = 0xAAAAAAAA;
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA;
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF95FFBA;
gpio->GPGUP = 0x0000FFFF;
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF;
;
初始化bd结构体中的bi_arch_numberbi_boot_params
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
;
启用指令和数据cache
;
通过对协处理器的操作了实现cache的使能
icache_enable();
dcache_enable();

return 0;
}
int interrupt_init (void)
{
;
获取计时控制寄存器
S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();
;
使用PWM定时器4
/* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */
timers->TCFG0 = 0x0f00;
if (timer_load_val == 0)
{
/*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and 15625 @ 50 MHz
*/
timer_load_val = get_PCLK()/(2 * 16 * 100);
}
/* load value for 10 ms timeout */
lastdec = timers->TCNTB4 = timer_load_val;
/* auto load, manual update of Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
/* auto load, start Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
timestamp = 0;

return (0);
}
static int env_init (void)
{
int crc1, crc1_ok;
uchar *addr1;

int crc2, crc2_ok;
uchar flag1, flag2, *addr2;
//
解析参数,定义了两个envdev_t型变量
//typedef struct envdev_s {
//uchar devname[16]; /* Device name */
//ulong devoff; /* Device offset */
//ulong env_size; /* environment size */
//ulong erase_size; /* device erase size */
//} envdev_t;
//
程序中定义了/dev/mtd1/dev/mtd2两个,parse_config函数用来初始化这两个结构体,并利用stat函数
//
初始化一个struct stat结构体
if (parse_config ()) /* should fill envdevices */
return 1;
//
为参数分配空间,ENV_SIZE=CFG_ENV_SIZE - ENV_HEADER_SIZE=0x10000-sizeof(unsigned long)
if ((addr1 = calloc (1, ENV_SIZE)) == NULL) {
fprintf (stderr,
"Not enough memory for environment (%ld bytes)n",
ENV_SIZE);
return (errno);
}
//
flash中读取参数
//typedef struct environment_s {
//ulong crc; /* CRC32 over data bytes */
//uchar flags; /* active or obsolete */
//uchar *data;
//} env_t;
/* read environment from FLASH to local buffer */
//
确定其指针
environment.data = addr1;
curdev = 0;
;
该函数用来将旧的参数从flash中擦除,然后将新的参数写入flash
if (flash_io (O_RDONLY)) {
return (errno);
}
;
进行crc校验
crc1_ok = ((crc1 = crc32 (0, environment.data, ENV_SIZE))
== environment.crc);
if (!HaveRedundEnv) {
if (!crc1_ok) {
fprintf (stderr,
"Warning: Bad CRC, using default environmentn");
memcpy(environment.data, default_environment, sizeof default_environment);
}
} else {
flag1 = environment.flags;

curdev = 1;
if ((addr2 = calloc (1, ENV_SIZE)) == NULL) {
fprintf (stderr,
"Not enough memory for environment (%ld bytes)n",
ENV_SIZE);
return (errno);
}
environment.data = addr2;

if (flash_io (O_RDONLY)) {
return (errno);
}

crc2_ok = ((crc2 = crc32 (0, environment.data, ENV_SIZE))
== environment.crc);
flag2 = environment.flags;

if (crc1_ok && !crc2_ok) {
environment.data = addr1;
environment.flags = flag1;
environment.crc = crc1;
curdev = 0;
free (addr2);
} else if (!crc1_ok && crc2_ok) {
environment.data = addr2;
environment.flags = flag2;
environment.crc = crc2;
curdev = 1;
free (addr1);
} else if (!crc1_ok && !crc2_ok) {
fprintf (stderr,
"Warning: Bad CRC, using default environmentn");
memcpy(environment.data, default_environment, sizeof default_environment);
curdev = 0;
free (addr1);
} else if (flag1 == active_flag && flag2 == obsolete_flag) {
environment.data = addr1;
environment.flags = flag1;
environment.crc = crc1;
curdev = 0;
free (addr2);
} else if (flag1 == obsolete_flag && flag2 == active_flag) {
environment.data = addr2;
environment.flags = flag2;
environment.crc = crc2;
curdev = 1;
free (addr1);
} else if (flag1 == flag2) {
environment.data = addr1;
environment.flags = flag1;
environment.crc = crc1;
curdev = 0;
free (addr2);
} else if (flag1 == 0xFF) {
environment.data = addr1;
environment.flags = flag1;
environment.crc = crc1;
curdev = 0;
free (addr2);
} else if (flag2 == 0xFF) {
environment.data = addr2;
environment.flags = flag2;
environment.crc = crc2;
curdev = 1;
free (addr1);
}
}
return (0);
}
//
如果参数中设置了波特率则利用参数用设置的波特率,否则利用默认的CONFIG_BAUDRATE(115200)
static int init_baudrate (void)
{
char tmp[64]; /* long enough for environment variables */
int i = getenv_r ("baudrate", tmp, sizeof (tmp));
gd->bd->bi_baudrate = gd->baudrate = (i > 0)
? (int) simple_strtoul (tmp, NULL, 10)
: CONFIG_BAUDRATE;

return (0);
}

int serial_init (void)
{
//
该函数设置uart的几个寄存器,包括FIFO寄存器、控制寄存器、列控制寄存器和波特率约数寄存器等
serial_setbrg ();
return (0);
}
;
设置gd->have_console
int console_init_f (void)
{
gd->have_console = 1;

#ifdef CONFIG_SILENT_CONSOLE
if (getenv("silent") != NULL)
gd->flags |= GD_FLG_SILENT;
#endif

return (0);
}
//
打印当前一些状态信息
static int display_banner (void)
{
printf ("nn%snn", version_string);
debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lXn",
_armboot_start, _bss_start, _bss_end);
#ifdef CONFIG_MODEM_SUPPORT
debug ("Modem Support enabledn");
#endif
#ifdef CONFIG_USE_IRQ
debug ("IRQ Stack: %08lxn", IRQ_STACK_START);
debug ("FIQ Stack: %08lxn", FIQ_STACK_START);
#endif

return (0);
}
//
初始化ram信息,设置起始地址和大小,从include/configs/smdk2410.h中获取这些信息
int dram_init (void)
{
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

return 0;
}
//
显示ram信息,其中的宏也是从include/configs/smdk2410.h中读取
static int display_dram_config (void)
{
int i;

#ifdef DEBUG
puts ("RAM Configuration:n");

for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);
print_size (gd->bd->bi_dram[i].size, "n");
}
#else
ulong size = 0;

for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
size += gd->bd->bi_dram[i].size;
}
puts("DRAM: ");
print_size(size, "n");
#endif

return (0);
}
//
以上都是一些初始化的函数,可以看出以上这些函数都是为了初始化一个全局的结构体变量gd而执行的,
//
该变量地址由寄存器r8指向,该结构体定义了开发板的相关硬件配置,在include/asm-arm/global_data.h
//
定义
/*
typedef struct global_data {
bd_t *bd;//
开发板参数
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! */
unsigned long bus_clk;
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t;
*/

void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
#ifndef CFG_NO_FLASH
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif

/* Pointer is writable since we allocated a register for it */
//
获取全局gd指针
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
//
清空该结构体
memset ((void*)gd, 0, sizeof (gd_t));
//
获取bd_info结构体指针
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
//
整个代码区的长度
monitor_flash_len = _bss_start - _armboot_start;
//
调用初始化函数,用来初始化gd结构体
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}

#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
//board/smdk2410/flash.c
配置flash
//
从其实现来看,好像只是配置nor flash
size = flash_init ();
//
显示flash信息
display_flash_config (size);
#endif /* CFG_NO_FLASH */
//
定义显示类型
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
//
按页对其方式保留显存
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */
//
显示器为LCD,同上
#ifdef CONFIG_LCD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = lcd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */
//
初始化CFG_MALLOC_LEN大小空间
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

//初始化nand flash,这是在nand flash启动的s3c2410移植u-boot的关键,根据flash时序编写函数即可
//
include/configs/smdk2410.h中的command definition中增加CONFIG_COMMANDSCFG_CMD_NAND命令
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND: ");
nand_init(); //board/smdk2410/smdk2410.c
#endif

#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif

/* initialize environment */
//
初始化环境参数
env_relocate ();
//framebuffer
初始化
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
//
通过命令行参数传递获取ip地址
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
//
通过命令行参数传递获取物理地址
/* MAC Address */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];

i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;

for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}

#ifdef CONFIG_HAS_ETH1
i = getenv_r ("eth1addr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;

for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
#endif
}
//
调用相应驱动函数对硬件设备进行初始化
devices_init (); /* get the devices list going. */

#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */

jumptable_init ();
//
初始化串口
console_init_r (); /* fully init console as a device */

#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif

/* enable exceptions */
//
启用中断
enable_interrupts ();

/* Perform network card initialisation if necessary */
//
初始化网卡
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif /* CFG_CMD_NET */

#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}

/* NOTREACHED - no way out of command loop except booting */
}
//
board/smdk2410/smdk2410.c中增加nand flash初始化代码
/*
* NAND flash initialization.
*/
//
include/s3c24x0.h中由S3C2410_NAND结构体的定义,也就是定义了NFCONFNFCMDNFADDRNFSTAT这四个//寄存器,由于2410flash控制器,因此可以直接通过flash控制器来对flash器件进行操作,如果芯片没有//flash控制器,有的利用I/O端口来同flash交换数据,就必须利用I/O口来模式时序。比如44B0X就是利用GPIO
//
来进行flash读写的
#if (CONFIG_COMMANDS & CFG_CMD_NAND)

typedef enum {
NFCE_LOW,
NFCE_HIGH,
} NFCE_STATE;

extern unsigned long nand_probe(unsigned long physadr);

static inline void NF_Reset(void)
{
int i;
//
使能
NF_SetCE(NFCE_LOW);
//
写入命令
NF_Cmd(0xFF); // reset command
//
延时
for(i = 0; i < 10; i++); // tWB = 100ns.
//
通过R/B信号线,判断flash内部操作是否完成,
NF_WaitRB(); // wait 200~500us;
//
芯片禁用
NF_SetCE(NFCE_HIGH);
}
static inline void NF_Init(void)
{
#if 0 // a little bit too optimistic
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
#else
#define TACLS 0
#define TWRPH0 4
#define TWRPH1 2
#endif
//
首先根据NFCONF寄存器的各位定义对Nand flash进行初始化
NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0));
NF_Reset();
}

void nand_init(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
NF_Init();
//
利用drivers/nand_legacy/nand_legacy.c,在该文件中也添加这几个同nand flash相关的函数
printf ("%4lu MBn", nand_probe((ulong)nand) >> 20);
}
//
操作控制寄存器,该寄存器为16位寄存器
static void NF_Conf(u16 conf)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCONF = conf;
}
//flash
只有8位的输入、输出端口,直接向命令寄存器赋值即可写入命令
static void NF_Cmd(u8 cmd)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCMD = cmd;
}

static void NF_CmdW(u8 cmd)
{
NF_Cmd(cmd);
udelay(1);
}
//
写入地址
static void NF_Addr(u8 addr)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFADDR = addr;
}
//
芯片使能
static void NF_SetCE(NFCE_STATE s)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
//Nand flash memroy chip enable,
如果NFCONF的第11位为1nFCE使能,为1则禁止
switch (s) {
case NFCE_LOW:
nand->NFCONF &= ~(1<<11);
break;
case NFCE_HIGH:
nand->NFCONF |= (1<<11);
break;
}
}
//
等待flash内部操作完成
static void NF_WaitRB(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
while (!(nand->NFSTAT & 0x01));
}
//
向数据寄存器写入数据
static void NF_Write(u8 data)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFDATA = data;
}
//
读取数据
static u8 NF_Read(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
return(nand->NFDATA);
}
//
校验
static void NF_Init_ECC(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCONF |= (1<<12);
}
//
获取ECC code
static u32 NF_Read_ECC(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
return(nand->NFECC);
}
#endif

通过上面的描述要移植U-Boots3c2410上,并要从nand flash启动,则需要修改的地方有
1
、在start.S中增加将U-Bootflash复制到ram中的代码
2
、在include/configs/smdk2410.h中增加相关定义,主要是nand flash寄存器的定义,并针对具体平台对其中的参数进行配置,比如ram的大小等。
3
、在board/smdk2410/smdk2410.c中增加nand flash的相关操作函数,如复位、写指令、读数据、查看状态等。
再编译U-Boot则可以看到flash的定义,并能通过网络下载内核和文件系统等。
U-Boot进入main_loop后将等待用户输入,或者在定时时间超过时调用do_bootm_linux来启动linux内核,
U-Boot
为各个处理器的do_bootm_linux定义了固定的类型,定义了boot_os_Fcn函数类型。
typedef void boot_os_Fcn (cmd_tbl_t *cmdtp, int flag,
int argc, char *argv[],
ulong addr, /* of image to boot */
ulong *len_ptr, /* multi-file image length table */
int verify); /* getenv("verify")[0] != 'n' */
cmdtp
结构体用来保存命令以及参数,addr为启动镜像的地址,程序首先进入do_bootm函数
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong iflag;
ulong addr;
ulong data, len, checksum;
ulong *len_ptr;
//
定义最大的解压空间为8M
uint unc_len = CFG_BOOTM_LEN;
int i, verify;
char *name, *s;
int (*appl)(int, char *[]);
//
镜像文件的头部信息,传递给do_bootm_linux函数
image_header_t *hdr = &header;

s = getenv ("verify");
verify = (s && (*s == 'n')) ? 0 : 1;
//
如果没有指定运行地址,则采用默认的地址,该地址在include/configs/smdk2410.h中定义为0x30008000
if (argc < 2) {
addr = load_addr;
} else {
//
如果参数中指定了地址,则从指定地址开始运行
addr = simple_strtoul(argv[1], NULL, 16);
}
//
在文件开头定义CONFIG_SHOW_BOOT_PROGRESS函数用来显示当前内核运行到哪一步了
SHOW_BOOT_PROGRESS (1);
printf ("## Booting image at %08lx ...n", addr);
//
系统中没有atmel的数据flash
/* Copy header so we can blank CRC field for re-calculation */
#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr)){
read_dataflash(addr, sizeof(image_header_t), (char *)&header);
} else
#endif
//
从镜像读取头部到结构header
memmove (&header, (char *)addr, sizeof(image_header_t));
//
通过头部结构体来获取magiccrc等数据
if (ntohl(hdr->ih_magic) != IH_MAGIC) {
//i386
#ifdef __I386__ /* correct image format not implemented yet - fake it */
//
通过头部结构体,检测镜像的格式是否正确
if (fake_header(hdr, (void*)addr, -1) != NULL) {
/* to compensate for the addition below */
addr -= sizeof(image_header_t);
/* turnof verify,
* fake_header() does not fake the data crc
*/
verify = 0;
} else
#endif /* __I386__ */
//
对于非i386平台
{
puts ("Bad Magic Numbern");
SHOW_BOOT_PROGRESS (-1);
return 1;
}
}
//magic
正确为第2
SHOW_BOOT_PROGRESS (2);
//
校验crc
data = (ulong)&header;
len = sizeof(image_header_t);

checksum = ntohl(hdr->ih_hcrc);
hdr->ih_hcrc = 0;

if (crc32 (0, (uchar *)data, len) != checksum) {
puts ("Bad Header Checksumn");
SHOW_BOOT_PROGRESS (-2);
return 1;
}
//
校验成功为第3
SHOW_BOOT_PROGRESS (3);

#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash(addr)){
len = ntohl(hdr->ih_size) + sizeof(image_header_t);
read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
addr = CFG_LOAD_ADDR;
}
#endif

//对于多文件镜像的处理,这个部分是很有用的,特别对于容量很小的系统,有时候需要将代码段和数据段放//在不同的文件中

//
显示镜像文件的信息,包括文件大小、创建时间、入口等
print_image_hdr ((image_header_t *)addr);
//data
为镜像文件头部后的第1ulong数据,用来完成crc校验
data = addr + sizeof(image_header_t);
//len
为镜像问价的大小
len = ntohl(hdr->ih_size);
//
是否进行crc校验
if (verify) {
puts (" Verifying Checksum ... ");
if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
printf ("Bad Data CRCn");
SHOW_BOOT_PROGRESS (-3);
return 1;
}
puts ("OKn");
}
//
校验成功为第4
SHOW_BOOT_PROGRESS (4);
//
数据指针
len_ptr = (ulong *)data;
//
校验cpu的体系结构
#if defined(__PPC__)
if (hdr->ih_arch != IH_CPU_PPC)
#elif defined(__ARM__)
if (hdr->ih_arch != IH_CPU_ARM)
#elif defined(__I386__)
if (hdr->ih_arch != IH_CPU_I386)
#elif defined(__mips__)
if (hdr->ih_arch != IH_CPU_MIPS)
#elif defined(__nios__)
if (hdr->ih_arch != IH_CPU_NIOS)
#elif defined(__M68K__)
if (hdr->ih_arch != IH_CPU_M68K)
#elif defined(__microblaze__)
if (hdr->ih_arch != IH_CPU_MICROBLAZE)
#elif defined(__nios2__)
if (hdr->ih_arch != IH_CPU_NIOS2)
#elif defined(__blackfin__)
if (hdr->ih_arch != IH_CPU_BLACKFIN)
#else
# error Unknown CPU type
#endif
{
printf ("Unsupported Architecture 0x%xn", hdr->ih_arch);
SHOW_BOOT_PROGRESS (-4);
return 1;
}
//
体系结构校验成功为第5
SHOW_BOOT_PROGRESS (5);
//
判断镜像文件类型
switch (hdr->ih_type) {
//
如果是独立的应用程序则利用第2个参数来作为加载地址
case IH_TYPE_STANDALONE:
name = "Standalone Application";
/* A second argument overwrites the load address */
if (argc > 2) {
hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
}
break;
//
单文件类型内核
case IH_TYPE_KERNEL:
name = "Kernel Image";
break;
//
多文件类型内核
case IH_TYPE_MULTI:
name = "Multi-File Image";
//
获取内核镜像大小
len = ntohl(len_ptr[0]);
/* OS kernel is always the first image */
data += 8; /* kernel_len + terminator */
for (i=1; len_ptr[i]; ++i)
data += 4;
break;
default: printf ("Wrong Image Type for %s commandn", cmdtp->name);
SHOW_BOOT_PROGRESS (-5);
return 1;
}
//
类型判断成功为第6
SHOW_BOOT_PROGRESS (6);

/*
* We have reached the point of no return: we are going to
* overwrite all exception vector code, so we cannot easily
* recover from any failures any more...
*/
//
关闭所有中断
iflag = disable_interrupts();
//
处理cache
#ifdef CONFIG_AMIGAONEG3SE
/*
* We've possible left the caches enabled during
* bios emulation, so turn them off again
*/
icache_disable();
invalidate_l1_instruction_cache();
flush_data_cache();
dcache_disable();
#endif
//
判断镜像压缩类型,并利用对应程序对内核压缩镜像进行解压
switch (hdr->ih_comp) {
case IH_COMP_NONE:
if(ntohl(hdr->ih_load) == addr) {
printf (" XIP %s ... ", name);
} else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
size_t l = len;
void *to = (void *)ntohl(hdr->ih_load);
void *from = (void *)data;

printf (" Loading %s ... ", name);

while (l > 0) {
size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
WATCHDOG_RESET();
memmove (to, from, tail);
to += tail;
from += tail;
l -= tail;
}
#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
}
break;
case IH_COMP_GZIP:
printf (" Uncompressing %s ... ", name);
if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
(uchar *)data, &len) != 0) {
puts ("GUNZIP ERROR - must RESET board to recovern");
SHOW_BOOT_PROGRESS (-6);
do_reset (cmdtp, flag, argc, argv);
}
break;
#ifdef CONFIG_BZIP2
case IH_COMP_BZIP2:
printf (" Uncompressing %s ... ", name);
/*
* If we've got less than 4 MB of malloc() space,
* use slower decompression algorithm which requires
* at most 2300 KB of memory.
*/
i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
&unc_len, (char *)data, len,
CFG_MALLOC_LEN < (4096 * 1024), 0);
if (i != BZ_OK) {
printf ("BUNZIP2 ERROR %d - must RESET board to recovern", i);
SHOW_BOOT_PROGRESS (-6);
udelay(100000);
do_reset (cmdtp, flag, argc, argv);
}
break;
#endif /* CONFIG_BZIP2 */
default:
//
重新打开中断
if (iflag)
enable_interrupts();
//
不能识别的压缩类型
printf ("Unimplemented compression type %dn", hdr->ih_comp);
SHOW_BOOT_PROGRESS (-7);
return 1;
}
puts ("OKn");
//
解压成功为第7
SHOW_BOOT_PROGRESS (7);
//
如果是独立的应用程序,则根据autostart来确定是否自动启动程序
switch (hdr->ih_type) {
case IH_TYPE_STANDALONE:
if (iflag)
enable_interrupts();

/* load (and uncompress), but don't start if "autostart"
* is set to "no"
*/
if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
char buf[32];
sprintf(buf, "%lX", len);
setenv("filesize", buf);
return 0;
}
//hdr->ih_ep
为程序的入口
appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
//
启动程序
(*appl)(argc-1, &argv[1]);
return 0;
case IH_TYPE_KERNEL:
case IH_TYPE_MULTI:
/* handled below */
break;
default:
if (iflag)
enable_interrupts();
printf ("Can't boot image type %dn", hdr->ih_type);
SHOW_BOOT_PROGRESS (-8);
return 1;
}
//
成功为第8
SHOW_BOOT_PROGRESS (8);
//
判断操作系统类型
switch (hdr->ih_os) {
default: /* handled by (original) Linux case */
case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
fixup_silent_linux();
#endif
printf("boot linuxn");
//
进入armlinux.c中的do_bootm_linux函数
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case IH_OS_NETBSD:
do_bootm_netbsd (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;

#ifdef CONFIG_LYNXKDI
case IH_OS_LYNXOS:
do_bootm_lynxkdi (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#endif

case IH_OS_RTEMS:
do_bootm_rtems (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;

#if (CONFIG_COMMANDS & CFG_CMD_ELF)
case IH_OS_VXWORKS:
do_bootm_vxworks (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
case IH_OS_QNX:
do_bootm_qnxelf (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#endif /* CFG_CMD_ELF */
#ifdef CONFIG_ARTOS
case IH_OS_ARTOS:
do_bootm_artos (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
#endif
}

SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG
puts ("n## Control returned to monitor - resetting...n");
do_reset (cmdtp, flag, argc, argv);
#endif
return 1;
}

进入文件lib_arm/arm_linux.c中的do_bootm_linux
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)

{
ulong len = 0, checksum;
ulong initrd_start, initrd_end;
ulong data;
//
内核调用入口
void (*theKernel)(int zero, int arch, uint params);
//
获取镜像的头部信息,headercmd_bootm.c中定义
image_header_t *hdr = &header;
//
获取开发板信息
bd_t *bd = gd->bd;

#ifdef CONFIG_CMDLINE_TAG
//
获取bootargs参数
char *commandline = getenv ("bootargs");
#endif
//
调用内核入口函数,将内核入口函数指针赋给theKernel
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

/*
* Check if there is an initrd image
*/
//
看是否为initrd镜像
if (argc >= 3) {
//
如果为initrd为第9
SHOW_BOOT_PROGRESS (9);
//
获取initrd镜像地址
addr = simple_strtoul (argv[2], NULL, 16);

printf ("## Loading Ramdisk Image at %08lx ...n", addr);

/* Copy header so we can blank CRC field for re-calculation */
#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash (addr)) {
read_dataflash (addr, sizeof (image_header_t),
(char *) &header);
} else
#endif
//
获取initrd头部西你想
memcpy (&header, (char *) addr,
sizeof (image_header_t));
//
校验magic
if (ntohl (hdr->ih_magic) != IH_MAGIC) {
printf ("Bad Magic Numbern");
SHOW_BOOT_PROGRESS (-10);
do_reset (cmdtp, flag, argc, argv);
}
//
校验crc
data = (ulong) & header;
len = sizeof (image_header_t);

checksum = ntohl (hdr->ih_hcrc);
hdr->ih_hcrc = 0;

if (crc32 (0, (unsigned char *) data, len) != checksum) {
printf ("Bad Header Checksumn");
SHOW_BOOT_PROGRESS (-11);
do_reset (cmdtp, flag, argc, argv);
}
//
校验成功为第10
SHOW_BOOT_PROGRESS (10);

print_image_hdr (hdr);

data = addr + sizeof (image_header_t);
len = ntohl (hdr->ih_size);

#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash (addr)) {
read_dataflash (data, len, (char *) CFG_LOAD_ADDR);
data = CFG_LOAD_ADDR;
}
#endif
//checksum
if (verify) {
ulong csum = 0;

printf (" Verifying Checksum ... ");
csum = crc32 (0, (unsigned char *) data, len);
if (csum != ntohl (hdr->ih_dcrc)) {
printf ("Bad Data CRCn");
SHOW_BOOT_PROGRESS (-12);
do_reset (cmdtp, flag, argc, argv);
}
printf ("OKn");
}
//
成功为第11
SHOW_BOOT_PROGRESS (11);
//
判断操作系统类型
if ((hdr->ih_os != IH_OS_LINUX) ||
(hdr->ih_arch != IH_CPU_ARM) ||
(hdr->ih_type != IH_TYPE_RAMDISK)) {
printf ("No Linux ARM Ramdisk Imagen");
SHOW_BOOT_PROGRESS (-13);
do_reset (cmdtp, flag, argc, argv);
}

#if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)
/*
*we need to copy the ramdisk to SRAM to let Linux boot
*/
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
data = ntohl(hdr->ih_load);
#endif /* CONFIG_B2 || CONFIG_EVB4510 */

/*
* Now check if we have a multifile image
*/
}
//
判断是否为多文件内核镜像
else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
ulong tail = ntohl (len_ptr[0]) % 4;
int i;

SHOW_BOOT_PROGRESS (13);

/* skip kernel length and terminator */
data = (ulong) (&len_ptr[2]);
/* skip any additional image length fields */
for (i = 1; len_ptr[i]; ++i)
data += 4;
/* add kernel length, and align */
data += ntohl (len_ptr[0]);
if (tail) {
data += 4 - tail;
}

len = ntohl (len_ptr[1]);

} else {
/*
* no initrd image
*/
//
initrd镜像
SHOW_BOOT_PROGRESS (14);

len = data = 0;
}

#ifdef DEBUG
if (!data) {
printf ("No initrdn");
}
#endif
//
确定initrd的起始地址和终止地址
if (data) {
initrd_start = data;
initrd_end = initrd_start + len;
} else {
initrd_start = 0;
initrd_end = 0;
}
//
成功为第15
SHOW_BOOT_PROGRESS (15);
//
进入linux内核
debug ("## Transferring control to Linux (at address %08lx) ...n",
(ulong) theKernel);

#if defined (CONFIG_SETUP_MEMORY_TAGS) ||
defined (CONFIG_CMDLINE_TAG) ||
defined (CONFIG_INITRD_TAG) ||
defined (CONFIG_SERIAL_TAG) ||
defined (CONFIG_REVISION_TAG) ||
defined (CONFIG_LCD) ||
defined (CONFIG_VFD)
//
开始设置启动参数,该函数也在本文件中定义,见下面
setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
//
设置串口参数
setup_serial_tag (&params);
#endif
#ifdef CONFIG_REVISION_TAG
//
设置修订参数
setup_revision_tag (&params);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
//
设置内存参数
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
//
设置命令行参数
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
//
设置initrd参数
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
//
设置vfdlcd参数
setup_videolfb_tag ((gd_t *) gd);
#endif
//
结束参数设置
setup_end_tag (bd);
#endif
//
启动内核
/* we assume that the kernel is in place */
printf ("nStarting kernel ...nn");

#ifdef CONFIG_USB_DEVICE
{
extern void udc_disconnect (void);
udc_disconnect ();
}
#endif
//
在内核启动前的清除工作
cleanup_before_linux ();
//
调用内核
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
}


//
开始设置启动参数
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params;

params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);

params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;

params = tag_next (params);
}