注:本文是学习朱老师课程整理的笔记,基于uboot-1.3.4和s5pc11x分析。
由uboot.lds链接脚本,我们知道整个程序的入口取决于中ENTRY声明的地方。
ENTRY(_start)
因此_start符号所在的文件就是整个程序的起始文件,_start所在的代码就是整个程序的起始代码。
_start在start.s中的内容:
.globl _start
_start: b reset
在start.s的一开始是头文件的包含:
#include
#include
#if defined(CONFIG_ENABLE_MMU)
#include
#endif
#include
#include
:config.h是在include目录下的,是配置过程中自动生成的文件(详见mkconfig脚本)。这个文件的内容其实是包含了一个头文件:
#include"
因此,#include
就是:include/configs/x210_sd.h
,这个文件里面是好多宏,整个uboot移植时的配置文件。比如上面的CONFIG_ENABLE_MMU就可以去x210_sd.h的文件中查找是否有定义。
#include
:在include/version.h中包含了include “version_autogenerated.h”这一句,这个头文件就是配置过程中自动生成的。里面就一行内容:#define U_BOOT_VERSION “U-Boot 1.3.4”。这里面定义的宏U_BOOT_VERSION的值是一个字符串,字符串中的版本号信息来自于Makefile中的配置值。这个宏在程序中会被调用,在uboot启动过程中会串口打印出uboot的版本号,那个版本号信息就是从这来的。
#include
:asm目录不是uboot中的原生目录,uboot中本来是没有这个目录的。asm目录是配置时创建的一个符号链接,实际指向的是就是asm-arm(详解见uboot下mkconfig脚本的分析)。而proc指向的是proc-armv目录,最终实际文件是:include/asm-arm/proc-armv/domain.h
#include
这个头文件也是配置过程中自动生成的,指向include/s5pc110.h。
从分析可以看出之前配置时创建的符号链接的作用,如果没有这些符号链接则编译时根本通不过,因为找不到头文件。(所以uboot不能在windows的共享文件夹下配置编译,因为windows中没有符号链接)
思考:为什么start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。
这样的设计主要是为了可移植性。因为如果直接包含,则start.S文件和CPU架构(和硬件)有关了,可移植性就差了。譬如我要把uboot移植到mips架构下,则start.S源代码中所有的头文件包含全部要修改。我们用了符号链接之后,则start.S中源代码不用改,只需要更改具体的硬件配置,更改创建的符号链接的指向,则可以具有可移植性。
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
#endif
.word是GNU汇编的伪汇编指令,相当于一个int类型。这里有4个.word,总共占据了16个字节。
在SD卡启动/Nand启动整个镜像,开头需要16字节的校验头。(mkv210image.c中就是为了计算这个校验头)。如果是usb启动直接下载的方式启动的则不需要16字节校验头(irom application note)。
这个占位的16字节只是保证正式的image的头部确实有16字节,但是这16字节的内容是不对的,还是需要后面去计算校验和然后重新填充的。
.globl _start
_start: b reset
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
异常向量表是硬件决定的,软件只是参照硬件的设计来实现它。异常向量表中每种异常都应该被处理,否则真遇到了这种异常就跑飞了。但是在uboot中并未非常细致的处理各种异常。下面有这些向量异常的处理函数入口地址:
_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
复位异常处的代码是:b reset,因此在CPU复位后真正去执行的有效代码是reset处的代码,因此reset符号处才是真正意义上的代码开始的地方。其中 reset是一个标号,在start.s下面有:
/* the actual reset code*/
reset:
/* set the cpu to SVC32 mode and IRQ & FIQ disable*/
/*前面4句被注释掉*/
@;mrs r0,cpsr
@;bic r0,r0,#0x1f
@;orr r0,r0,#0xd3
@;msr cpsr,r0
msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
其实ARM CPU在复位时默认就会进入SVC模式,但是这里还是使用软件将其置为SVC模式。整个uboot工作时CPU一直处于SVC模式。
_end_vect:
.balignl 16,0xdeadbeef
.balignl 16,0xdeadbeef. 这一句指令是让当前地址16字节对齐,如果当前地址不对齐则自动向后走地址直到对齐,并且向后走的那些内存要用0xdeadbeef来填充。0xdeadbeef这是一个十六进制的数字,这个数字很有意思,刚好组成了英文的dead beef这两个单词,字面意思是坏牛肉。
_TEXT_BASE:
.word TEXT_BASE
TEXT_BASE就是Makefile配置阶段写入的TEXT_BASE值,其实就是我们链接时指定的uboot的链接地址。
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE #uboot在DDR中的物理地址
cpu_init_crit:
bl disable_l2cache /*禁止L2 cache*/
bl set_l2cache_auxctrl_cycle /*l2 cache相关初始化*/
bl enable_l2cache /*使能l2 cache*/
/* Invalidate L1 icache and dcache*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
/*disable MMU stuff and caches*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
为什么上面的bic和orr指令都连续执行了2次。因为0x2007和0x802都是非法立即数,不可以直接操作。
上面这几步都是和CPU的cache和mmu有关的,不用去细看,大概知道即可。
/* Read booting information */
ldr r0, =PRO_ID_BASE
ldr r1, [r0,#OMR_OFFSET] /* r1=0xE0000004 */
bic r2, r1, #0xffffffc1 NAND BOOT */
cmp r2, #0x0 @ 512B 4-cycle
/* nand的页大小为512B,4个指令周期 */
moveq r3, #BOOT_NAND
cmp r2, #0x2 @ 2KB 5-cycle
moveq r3, #BOOT_NAND
cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC
/* nand的页大小为4KB,5个指令周期,8位ECC校验 */
moveq r3, #BOOT_NAND
cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC
moveq r3, #BOOT_NAND
cmp r2, #0x8 @ OneNAND Mux
moveq r3, #BOOT_ONENAND
/* SD/MMC BOOT */
cmp r2, #0xc
moveq r3, #BOOT_MMCSD
/* NOR BOOT */
cmp r2, #0x14
moveq r3, #BOOT_NOR
在x210内部有一个寄存器(地址是0xE0000004),这个寄存器中的值决定真正的启动介质是谁。这个寄存器中的值是硬件根据OM引脚的设置而自动设置值的。在r2寄存器中存储了一个数字,这个数字代表不同的启动介质,然后r3中存储相对应的数值,以后备用。
下面的分析见:uboot之start.s分析2