4.3.1U-Boot代码结构分析
4.3 Bootloader之U-Boot
U-Boot是在PPC-Boot的基础上进化⽽来的⼀个开放源码的嵌⼊式BootROM程序,本⼩节将使⽤1.1.4版本的代码来分析U-Boot的代码结构,以及如何将它移植到基于S3C2410X的开发板上来。
4.3.1 U-Boot代码结构分析
U-Boot4-9所⽰。
board:这个⽬录存放了所有U-Boot⽀持的⽬标板的⼦⽬录,如board/smdk2410/*就和我们的开发平台fs2410相类似。要将U-Boot移植到⾃⼰的S3C2410X⽬标板上,必须参考这个⽬录下的内容,⽐如对Flash 以及Flash宽度和⼤⼩的定制等就要修改其中的flash.c。
cpu:这个⽬录存放了U-Boot⽀持的CPU类型,因为我们的开发平台是S3C2410X,所以只关⼼
cpu/arm920t,CPU相关的⽂件主要是初始化⼀个执⾏环境,包括中断的初始化;start.S是整个u-boot.bin ⽬标可执⾏代码的第⼀段代码,它们是从Flash开始运⾏的,其主要⼯作就是对整个U-Boot⽬标代码的重定位,即将U-Boot转移到内存中去运⾏。
common:这个⽬录存放了U-Boot的⼀些公共命令的实现,像那些以cmd_*.c为名字的⽂件就是对应U-Boot的每个命令的实现代码,我们通常关⼼cmd_boot.c和cmd_bootm.c(它们和内核的引导相关)。
drivers:这个⽬录中存放了各种外设接⼝的驱动程序。
fs:这个⽬录中存放了U-Boot⽀持的⽂件系统。
lib_arm:这个⽬录存放了ARM平台公共的接⼝代码。
include:这个⽬录存放头⽂件的公共⽬录,其中的include/configs/smdk2410.h定义了所有和S3C2410X 相关的资源的配置参数,我们往往只需修改这个⽂件就可以配置⽬标板的参数,如波特率、引导参数、物理内存映射等。u-boot-1.1.4的软件包解压后⼤约70MB,⾮常庞⼤。但是当我们选择了⾃⼰的平台后,要处理的⽂件并不是很多,编译后的u-boot.bin⽂件也只有⼏⼗KB左右。
4.3.2 编译U-Boot代码
编译U-Boot和编译vivi⼀样,简单地输⼊"make"命令就可以了。要注意的是,U-Boot提供了对多种平台的⽀持,因此在编译之前,需要进⾏平台选择。下⾯的错误就是因为没有选择平台造成的:
2.System not configured -e README
3.make: *** [all] Error 1
我们需要选择平台,如:
stahl2.Configuring for smdk2410 board ...
在上述命令中,smdk2410_config是定义在Makefile⽂件中的假想⽬标,也就是说make命令的参数⼀定是在Makefile⽂件中指定的。下⾯是从Makefile⽂件中摘出来的⽚断代码:
1.smdk2410_config : unconfig
2. @./mkconfig $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
3.
4.ep7312_config : unconfig
5. @./mkconfig $(@:_config=) arm arm720t ep7312
6.
7.at91rm9200dk_config : unconfig
8. @./mkconfig $(@:_config=) arm at91rm9200 at91rm9200dk
日语作文smdk2410_config、ep7312_config和at91rm9200dk_config⽤来定义三种平台,我们可以把它们传递给make,以确定要编译的⽬标,具体编译的⽂件在后⾯⼀⾏指定。其中的arm920t,s3c24x0和ep73
12都可以在源码的cpu⽬录中找到。mkconfig是⼀个脚本程序,⽤来创建在configure过程中需要使⽤的头⽂件和链接⽂件。
如果编译过程中没有出现错误,那么将创建U-Boot⽂件,并通过objcopy将其转换为⼆进制格式和⼗六进制Motorola S-records⽂件格式。最后将u-boot.bin下载到开发板的Flash中引导操作系统,如图4-10所⽰。U-Boot不仅可以在NOR Flash上运⾏,也可以在NAND Flash中运⾏,⼀切取决于代码的编写。
4.3.3 U-Boot代码导读
从本节开始将分析U-Boot的代码,并把U-Boot移植到FS2410开发板上,更重要的是通过对这些和硬件紧密相关的代码进⾏分析,进⼀步熟悉Bootloader的作⽤和ARM处理器。
4.3.3.1 CPU⽬录(1)
U-Boot是从cpu/arm920t/start.S开始的,这个⽂件的任务是设置处理器状态、初始化中断和内存时序等,并确定是否需要对整个U-Boot代码重定位,最终从Flash中跳转到定位好的内存位置执⾏。
开始的⼀段代码是处理器的异常处理向量表。
2._start: b ret ;各个异常向量对应的跳转代码
3. ldr pc, _undefined_instruction ; 未定义的指令异常
4. ldr pc, _software_interrupt ; 软件中断异常
5. ldr pc, _prefetch_abort ; 内存操作异常
初中英语单词mp3
6. ldr pc, _data_abort ; 数据异常
7. ldr pc, _not_ud ; 未使⽤
8. ldr pc, _irq ; 慢速中断异常
9. ldr pc, _fiq ; 快速中断异常斟酌
10._undefined_instruction: .word undefined_instruction
11._software_interrupt: .word software_interrupt
12._prefetch_abort: .word prefetch_abort
13._data_abort: .word data_abort
14._not_ud: .word not_ud
15._irq: .word irq
16._fiq: .word fiq
17. .balignl 16,0xdeadbeef
下⾯的语句很重要:tough是什么意思
1.TEXT_BASE:
2. .word TEXT_BASE ;这个值在/board/smdk2410/config.mk中定义
初一英语上册知识点3..globl _armboot_start
4._armboot_start:
5..word _start ;代码段的起始地址也是TEXT_BASE
6.globl _bss_start
7._bss_start:
8. .word __bss_start ;由链接程序ld根据
链接脚本确定(/board/smdk2410/u-boot.lds)
10._bss_end:
11. .word _end ;由链接程序ld根据链接脚本确定
接下来是系统复位,这个过程可参考处理器的⼿册。
<:
2. mrs r0,cpsr
3. bic r0,r0,#0x1f
4. orr r0,r0,#0xd3
5. msr cpsr,r0
接下来的代码⽤来关闭看门狗,通过INTMR寄存器屏蔽所有的中断,并配置处理器内部时钟频率,需要仔细研究并深刻理解的是关于重定向(relocate)的代码。
重定向(relocate)代码将Bootloader⾃⾝由Flash复制到SDRAM,以便跳转到SDRAM执⾏。进⾏重定向的原因之⼀是因为程序在Flash中执⾏速度⽐较慢,⽽系统复位后总是从0x00000000地址取指。
下⾯的代码⾸先判断是否需要进⾏重定位,如果需要的话⾸先确定复制的源基址、源⼤⼩和⽬标基址,然后通过寄存器r3 ~ r10,将代码复制到SDRAM中。i am legend
2. adr r0, _start /* r0 <-当前的代码位置 */
3. ldr r1, _TEXT_BASE /* 测试是否从Flash到RAM,_TEXT_BASE在
4.board/smdk2410/config.mk中定义 */
5. cmp r0, r1 /* 如果是调试阶段则不进⾏重定向 */
6. beq stack_tup
7. ldr r2, _armboot_start /* _armboot_start
时域
在前⾯定义,是第⼀条指令的运⾏地址 */
8. ldr r3, _bss_start /* 在连接脚本u-boot.
lds中定义,是代码段的结束地址 */
9. sub r2, r3, r2 /* r2 <- armboot⼤⼩ */
10. add r2, r0, r2 /* r2 <-源代码结束地址 */
12. ldmia r0!, {r3-r10} /* 从源地址处[r0]复制 */
13. stmia r1!, {r3-r10} /*复制到⽬的地址[r1] */
14. cmp r0, r2 /* 判断是否复制完毕 */
15. ble copy_loop
提⽰
调试阶段的代码是直接在RAM中运⾏的,⽽最后需要把这些代码固化到Flash中,因此U-Boot需要⾃⼰从Flash转移到RAM中运⾏。这也是重定向的⽬的所在。
其中关键的⼀步就是通过adr指令得到当前代码的地址信息:如果U-Boot是从RAM开始运⾏,则从adr r0, _start得到的地址信息为r0 = _start = _TEXT_BASE = TEXT_BASE = 0x33F80000;如果U-Boot从Flash开始运⾏,即从处理器对应的地址运⾏,则r0 = 0x0000,这时将会执⾏copy_loop标识的那段代码了。
4.3.3.1 CPU⽬录(2)
下⼀步是建⽴堆栈:
1.stack_tup:
2. ldr r0, _TEXT_BASE
3. sub r0, r0, #CFG_MALLOC_LEN
4. sub r0, r0, #CFG_GBL_DATA_SIZE
5.#ifdef CONFIG_USE_IRQ
6. sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
7.#endif
8. sub sp, r0, #12
9.
10.clear_bss:
11. ldr r0, _bss_start
12. add r0, r0, #4
13. ldr r1, _bss_end
14. mov r2, #0x00000000
15.clbss_l:str r2, [r0]
16. add r0, r0, #4
17. cmp r0, r1
18. bne clbss_l
最终将通过下⾯的语句跳转到C代码执⾏,stage1的使命也算完成。
1.ldr pc, _start_armboot
2._start_armboot: .word start_armboot
inbox
start_armboot( )在lib_arm/board.c中定义,它类似于Linux内核的start_kernel( ),它们都是⼀种系统初始化的接⼝函数:在内核的start_kernel( )中集中完成了内核⼏乎所有资源的初始化,包括CPU相关的资源和外设接⼝等;⽽在start_armboot( )中也要完成⼀些初始化⼯作。
start.S中剩下的代码是对处理器的⼀些设置和中断处理。尽管⼜是⼀些晦涩的汇编指令,但幸运的是这些代码和移植关系不⼤,只要了解⼤概的⽤途即可。细⼼的读者可能会发现在前⽂的vivi代码中提到这些代码。这两种Bootloader要对同⼀种处理器完成同样的初始化⼯作。
宝宝早教
1.U-Boot mov r0, #0
2. mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 Cache */
3. mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
4.vivi "mov r0, #0\n"