现在从代码的角度来分析启动的流程:
从u-boot.lds文件知:
ENTRY(_start) SECTIONS { . = 0x00000000; . = ALIGN(4); .text : { *(.__image_copy_start) *(.vectors) CPUDIR/start.o (.text*) *(.text*) }
文件中__image_copy_start位映像文件复制起始地址,它在文件arch/arm/lib/sections.c中如下定义:
char __image_copy_start[0] __attribute__((section(".__image_copy_start"))); char __image_copy_end[0] __attribute__((section(".__image_copy_end")));
这个__image_copy_start在文件common/board_r.c中被调用,
static int initr_reloc_global_data(void) { #ifdef __ARM__ monitor_flash_len = _end - __image_copy_start;
文件中.vectors(英文矢量)在arch/arm/lib/vectors.S的文件中定义:
.section ".vectors", "x"
.section伪操作
用户可以通过.section伪操作来自定义一个段,格式如下:
.section section_name [, "flags"[, %type[,flag_specific_arguments]]]
每一个段以段名为开始, 以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),连接器可以识别这些标志。(与armasm中的AREA相同)。
下面是ELF格式允许的段标志
<标志> 含义
a 允许段
w 可写段
x 执行段
参考:http://blog.sina.com.cn/s/blog_59b189220100au1k.html
所以x表示可执行段
该段下面就是可执行代码的起始位置:
_start:
通过下面的语句跳转到reset的入口:
b reset
b汇编指令见:http://blog.chinaunix.net/uid-20799298-id-99633.html
先不跳入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
ldr汇编指令见:http://blog.chinaunix.net/uid-28458801-id-4084264.html
举其中一个存储器地址“_undefined_instruction”为例,它定义如下:
.globl _undefined_instruction .globl _software_interrupt .globl _prefetch_abort .globl _data_abort .globl _not_used .globl _irq .globl _fiq _undefined_instruction: .word undefined_instruction
undefined_instruction定义如下:
undefined_instruction: get_bad_stack bad_save_user_regs bl do_undefined_instruction
获取坏掉的栈,保存用户寄存器,有打印信息供开发人员调试,最后就是在死循环了。
下面我们再来看看reset代码段,reset定义在:arch/arm/cpu/arm920t/start.S文件中,reset(英文复位),系统复位时也会从这里开始,处理硬件上的复位,软件复位命令时通过看门狗操作实现。reset开始的代码段实现切换到超级用户模式(SVC 模式),接着是异常重定位异常表:
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) /* * relocate exception table */ ldr r0, =_start ldr r1, =0x0 mov r2, #16 copyex: subs r2, r2, #1 ldr r3, [r0], #4 str r3, [r1], #4 bne copyex #endif
CONFIG_AT91RM9200EK宏有定义,
LDR r0,=label
如果label是立即数,就把数值赋给r0, 如果lable是标识符,就把label地址的值赋给r0
r0保存了_start表示的地址,r1保存0,r2保存16,r2 = r2-1, subs中的s可以影响CPSR中的条件标志位,将r0地址处的内容读到r3中,再r0 = r0+4,str这条指令将R3中的字数据写入以R1为地址的存储器中,并将新地址R1+4写入R1,bne指令根据CPSR中的条件标志位判断是否跳转,如果subs指令中r2初值为16减为0时即影响了条件标志位,没有影响就跳转到copyex。所以异常重定位就是将__start开始的16个字拷贝到0地址开始的位置中去。
异常重定位结束后就是我们常说的关看门狗、屏蔽中断、设置系统时钟,
# if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interrupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */ #else # define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interrupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ # endif 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, =INTMSK str r1, [r0] # if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] # endif /* FCLK:HCLK:PCLK = 1:2:4 */ /* default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] #endif /* CONFIG_S3C24X0 */
下面是:
/* * we do sys-critical inits only at reboot, * not when booting from ram! */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif
从它的注释我们看到只在重启就进行系统临界初始化,我们计入cpu_init_curit查看。
明天再看。