NXP

u-boot分析 四 (程序入口start.S)

2019-07-12 13:29发布

u-boot分析 四 (程序入口start.S)


注:部分内容摘抄自网络,如有问题,请联络博主。
本文内容:了解以stars.S为开始的ARM汇编程序部分。
回顾前几篇博文,咱们见识过了u-boot的目录结构,另外简要分析了u-boot.lds脚本文件的link原理。而今天我们要来听听嵌入式程序君告诉咱们的第一句“话”。
正式开始之前,我们需要准备三样东西:
  1. u-boot source code
  2. 常用ARM指令集
  3. Source Insight(用于trace code,使用方法略过,不会问百度)
    这里写图片描述

bootloader通常stage1和stage2两步骤,u-boot也不例外。
  1. Stage1:依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在这个程序段,且可以用汇编语言来实现;
  2. stage2:通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
具体的说,
  1. Stage1 start.S代码结构
    u-boot的stage1代码通常放在start.S文件中,用汇编语言写成,其主要代码部分如下:
    (1) 定义入口。由于一个可执行文件必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址。因此,必须通知编译器以使其知道这个入口,该工作可通过修改链接器脚本(.lds文件)来完成。
    (2) 设置异常向量(Exception Vector)。
    (3) 设置CPU的速度、时钟频率及终端控制寄存器。
    (4) 初始化内存控制器。
    (5) 将ROM中的代码复制到RAM中。
    (6) 初始化堆栈。
    (7) 转到RAM中执行,该工作可使用指令ldr pc来完成。
  2. Stage2 C语言代码部分
    ./arch/arm/lib/board.c中的board_init_r()是C语言开始的函数,也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数主要完成如下操作:
    (1) 调用一系列的初始化函数。
    (2) 初始化Flash设备。
    (3) 初始化系统内存分配函数。
    (4) 如果目标系统拥有NAND设备,则初始化NAND设备。
    (5) 如果目标系统有显示设备,则初始化该类设备。
    (6) 初始化相关网络设备,填写IP、MAC地址等。
    (7) 进去命令循环(即整个boot的工作循环),接收用户从串口输入的命令,然后进行相应的工作。
事不宜迟,我们赶紧打开start.S:
./arch/arm/cpu/slsiap/s5p4418/start.S /* * armboot - Startup Code for NXPxxxx/ARM Cortex CPU-core */ #include #include #include #include #include /* ************************************************************************* * * Exception vectors as described in ARM reference manuals * * replace arm/lib/vectors.S * ************************************************************************* */ .globl _stext /*程序的全局入口,《u-boot分析 三》中u-boot.lds设置此入口地址为0x00000000*/ _stext: b reset /*参阅《常用ARM指令集及汇编》可知,b为跳转指令,跳转到reset函数处,reset在后面*/ /*ARM体系结构规定在上电复位后的起始位置,必须有8条连续的跳转指令,通过硬件实现。他们就是异常向量表*/ /*ldr,用于加载32bit的立即数或一个地址值到指定寄存器*/ ldr pc, _undefined_instruction /*未定义指令异常,0x04*/ ldr pc, _software_interrupt /*软中断异常,0x08*/ ldr pc, _prefetch_abort /*内存操作异常,0x0c*/ ldr pc, _data_abort /*数据异常,0x10*/ ldr pc, _not_used /*未使用,0x14*/ ldr pc, _irq /*慢速中断异常,0x18*/ ldr pc, _fiq /*快速中断异常,0x1c*/ /*.word的意思是将后边的符号所对应的32bit值赋予前面的符号*/ /*而如下的七条语句,后面的符号正好是对应的中断异常服务程序的入口地址*/ /*这七个中断服务程序位于./arch/arm/cpu/slsiap/s5p4418/vector.S*/ _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 /*16bytes对齐,并且使用0xdeadbeef填充*/ /*.balignl是.balign的变体 .align伪操作用于表示对齐方式:通过添加填充字节使当前位置满足一定的对齐方式。 .balign的作用同.align。 .align {alignment} {,fill} {,max} 其中: alignment用于指定对齐方式,可能的取值为2的次幂,缺省为4。 fill是填充内容,缺省用0填充。 max是填充字节数最大值,如果填充字节数超过max,就不进行对齐*/ /* ************************************************************************* * * Text and section base * ************************************************************************* */ .globl TEXT_BASE /*全局定义TEXT_BASE*/ TEXT_BASE: .word CONFIG_SYS_TEXT_BASE /*TEXT_BASE使用.word赋予的值为CONFIG_SYS_TEXT_BASE,而CONFIG_SYS_TEXT_BASE是在 u-boot/include/configs/s5p4418_urbetter.h中定义,值为0x42c00000*/ /* * These are defined in the board-specific linker script(u-boot.lds中定义),可参看前面的博客 */ .globl _bss_start_ofs _bss_start_ofs: .word __bss_start - _stext .globl _bss_end_ofs _bss_end_ofs: .word __bss_end - _stext .globl _end_ofs _end_ofs: .word _end - _stext /* ************************************************************************* * * Reset handling * ************************************************************************* */ .globl reset /*这里便是reset函数的入口*/ /*其实在CPU一上电以后就是跳到这里执行的*/ reset: bl save_boot_params /*call save_boot_params,此函数在下面有定义,但是实际do nothing*/ /* * set the CPU to SVC32 mode,即管理模式 */ /*对状态寄存器CPSR的修改要按照:读出-修改-写回的顺序来执行*/ /* 31 30 29 28 ----- 7 6 - 4 3 2 1 0 N Z C V I F M4 M3 M2 M1 M0 0 0 0 0 0 User26 模式 0 0 0 0 1 FIQ26 模式 0 0 0 1 0 IRQ26 模式 0 0 0 1 1 SVC26 模式 1 0 0 0 0 User 模式 1 0 0 0 1 FIQ 模式 1 0 0 1 0 IRQ 模式 1 0 0 1 1 SVC 模式 1 0 1 1 1 ABT 模式 1 1 0 1 1 UND 模式 1 1 1 1 1 SYS 模式 */ mrs r0, cpsr /*读出cpsr的值*/ bic r0, r0, #0x1f /*清零低5位*/ orr r0, r0, #0xd3 /*0xd3,即1 1 0 1 0 0 1 1,即为SVC模式,禁止IRQ,FIQ中断(bit6,7)*/ msr cpsr,r0 /*写回cpsr使其生效*/ /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT /*UT4418 u-boot source code中没有define CONFIG_SKIP_LOWLEVEL_INIT,所以如下两条命令会执行*/ bl cpu_init_cp15 /*bl为跳转指令,即call cpu_init_cp15函数,该函数在下面定义,主要功能是 Invalidate L1 I/D,disable MMU stuff and I-cache*/ bl cpu_init_crit /*继续调用cpu_init_crit函数,函数定义在下面,该函数中只有一句函数,即b lowlevel_init, lowlevel_init函数的具体功能,此处先略过*/ #endif #ifdef CONFIG_RELOC_TO_TEXT_BASE /*u-boot/include/configs/s5p4418_urbetter.h中define CONFIG_RELOC_TO_TEXT_BASE*/ relocate_to_text: /*程序继续执行,此程序段便是要将ROM中的code copy到TEXT_BASE处,即从0x0开始的数据copy到0x42c00000处*/ /* * relocate u-boot code on memory to text base * for nexell arm core (add by jhkim) */ adr r0, _stext /* r0 <- current position of code */ ldr r1, TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ beq clear_bss ldr r2, _bss_start_ofs add r2, r0, r2 /* r2 <- source end address */ copy_loop_text: 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_text ldr r1, TEXT_BASE /* restart at text base */ mov pc, r1 /*程序从TEXT_BASE处开始执行*/ clear_bss: ldr r0, _bss_start_ofs ldr r1, _bss_end_ofs ldr r4, TEXT_BASE /* text addr */ add r0, r0, r4 add r1, r1, r4 mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 bne clbss_l #ifdef CONFIG_MMU_ENABLE /*同样在u-boot/include/configs/s5p4418_urbetter.h中已经define*/ bl mmu_turn_on /*ROM到RAM的copy完成后,继续调用 mmu_turn_on函数,此函数位于/u-boot/arch/arm/cpu/slsiap/s5p4418/mmu_asm.S 主要功能是打开MMU,详细过程需要进一步trace code*/ #endif ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) /*u-boot/include/configs/s5p4418_urbetter.h中 define CONFIG_SYS_INIT_SP_ADDR = CONFIG_SYS_TEXT_BASE,即0x42c00000*/ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ sub sp, #GD_SIZE /* allocate one GD above SP */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ mov r9, sp /* GD is above SP */ mov r0, #0 bl board_init_f /*call board_init_f, /u-boot/arch/arm/lib/board.c*/ mov sp, r9 /* SP is GD's base address */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ sub sp, #GENERATED_BD_INFO_SIZE /* allocate one BD above SP */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ mov r0, r9 /* gd_t *gd */ ldr r1, TEXT_BASE /* ulong text */ mov r2, sp /* ulong sp */ bl gdt_reset /*调用gdt_reset函数初始化gdt,u-boot/arch/arm/cpu/slsisp/s5p4418/Cpu.c*/ /* call board_init_r(gd_t *id, ulong dest_addr) */ mov r0, r9 /* gd_t */ ldr r1, =(CONFIG_SYS_MALLOC_END) /* dest_addr for malloc heap end */ /* call board_init_r */ ldr pc, =board_init_r /* this is auto-relocated! */ /*board_init_r变开始进入Stage2,/u-boot/arch/arm/lib/board.c*/ #else /* CONFIG_RELOC_TO_TEXT_BASE */ bl _main #endif /*------------------------------------------------------------------------------*/ ENTRY(c_runtime_cpu_setup) /* * If I-cache is enabled invalidate it */ #ifndef CONFIG_SYS_ICACHE_OFF mcr p15, 0, r0, c7, c5, 0 @ invalidate icache mcr p15, 0, r0, c7, c10, 4 @ DSB mcr p15, 0, r0, c7, c5, 4 @ ISB #endif /* * Move vector table */ /* Set vector address in CP15 VBAR register */ ldr r0, =_stext mcr p15, 0, r0, c12, c0, 0 @Set VBAR bx lr ENDPROC(c_runtime_cpu_setup) /************************************************************************* * * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) * __attribute__((weak)); * * Stack pointer is not yet initialized at this moment * Don't save anything to stack even if compiled with -O0 * *************************************************************************/ ENTRY(save_boot_params) bx lr @ back to my caller ENDPROC(save_boot_params) .weak save_boot_params /************************************************************************* * * cpu_init_cp15 * * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless * CONFIG_SYS_ICACHE_OFF is defined. * *************************************************************************/ ENTRY(cpu_init_cp15) /* * Invalidate L1 I/D */ /*MRC,协处理器寄存器到ARM寄存器的数据传输指令。MRC将协处理器寄存器中的数据传送到 ARM处理器的寄存器中。若协处理器不能成功执行该操作,将产生未定义异常中断*/ mov r0, #0 /*set up for MCR*/ mcr p15, 0, r0, c8, c7, 0 /*invalidate TLBs,使TLBs无效*/ mcr p15, 0, r0, c7, c5, 0 /*invalidate icache,使icache无效*/ mcr p15, 0, r0, c7, c5, 6 /*invalidate BP array*/ /*如下两条命令,参考http://blog.csdn.net/itxiebo/article/details/50957808 了解即可,甚至可以直接跳过分析*/ dsb isb /* * 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 11 (Z---) BTB*/ #ifdef CONFIG_SYS_ICACHE_OFF bic r0, r0, #0x00001000 /*clear bit 12 (I) I-cache*/ #else orr r0, r0, #0x00001000 /*set bit 12 (I) I-cache*/ #endif mcr p15, 0, r0, c1, c0, 0 mov pc, lr /*back to my caller,返回调用处*/ ENDPROC(cpu_init_cp15) #ifndef CONFIG_SKIP_LOWLEVEL_INIT /************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * *************************************************************************/ ENTRY(cpu_init_crit) /* * Jump to board specific initialization... * The Mask ROM will have already initialized * basic memory. Go here to bump up clock rate and handle * wake up conditions. */ b lowlevel_init /*go setup pll,mux,memory*/ /*u-boot/arch/arm/cpu/slsiap/s5p4418/low_init.S*/ ENDPROC(cpu_init_crit) #endif
回顾全文,我们总结一下start.S都干了些什么?
  1. 首先定义程序入口_stext
  2. 然后定义了异常向量表
  3. 一堆初始化,e.g. PLL, MUX, Memory等
  4. Copy代码从ROM到RAM
  5. MMU on
  6. 最后跳转到C入口

参考博客:
http://blog.chinaunix.net/uid-22891435-id-380150.html