单纯的从源码开始分析uboot是一件十分令人头疼的事,因为uboot不只是针对你是用的芯片架构,你在分析源码时所遇到的那些条件编译你很难去找到它定义的地方,更别说这个宏到底有没有被定义,我个人比较喜欢对照着反汇编去分析源代码。
在顶层目录下执行:
make omapl138_lcdk_defconfig
make
- MEMORY { .sram : ORIGIN = 0x80000000, LENGTH = 131072 }
- OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
- OUTPUT_ARCH(arm)
- ENTRY(_start)
- SECTIONS
- {
- . = 0x00000000;
- . = ALIGN(4);
- .text :
- {
- __start = .;
- *(.vectors)
- arch/arm/cpu/arm926ejs/start.o (.text*)
- *(.text*)
- } >.sram
- . = ALIGN(4);
- .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram
- . = ALIGN(4);
- .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
- . = ALIGN(4);
- .u_boot_list : { KEEP(*(SORT(.u_boot_list*))); } >.sram
- . = ALIGN(4);
- .rel.dyn : {
- __rel_dyn_start = .;
- *(.rel*)
- __rel_dyn_end = .;
- } >.sram
- .bss :
- {
- . = ALIGN(4);
- __bss_start = .;
- *(.bss*)
- . = ALIGN(4);
- __bss_end = .;
- } >.sram
|
编译完成后,在目录spl下找到我们生成的u-boot-spl.lds链接脚本:
由链接脚本我们可以得出以下信息:
1.程序的入口地址在_start处
2._start入口函数在arch/arm/cpu/arm926ejs/start.S处定义
执行arm-linux-objdump -D u-boot-spl > u-boot-spl.dis
- 80000000 <__start>:
- 80000000: ea000016 b 80000060 <reset>
- 80000004: e59ff014 ldr pc, [pc, #20] ; 80000020 <_undefined_instruction>
- 80000008: e59ff014 ldr pc, [pc, #20] ; 80000024 <_software_interrupt>
- 8000000c: e59ff014 ldr pc, [pc, #20] ; 80000028 <_prefetch_abort>
- 80000010: e59ff014 ldr pc, [pc, #20] ; 8000002c <_data_abort>
- 80000014: e59ff014 ldr pc, [pc, #20] ; 80000030 <_not_used>
- 80000018: e59ff014 ldr pc, [pc, #20] ; 80000034 <_irq>
- 8000001c: e59ff014 ldr pc, [pc, #20] ; 80000038 <_fiq>
|
即生成u-boot-spl的反汇编文件:
- 80000060 <reset>:
- 80000060: e10f0000 mrs r0, CPSR
- 80000064: e3c0001f bic r0, r0, #31
- 80000068: e38000d3 orr r0, r0, #211 ; 0xd3
- 8000006c: e129f000 msr CPSR_fc, r0
- 80000070: eb000228 bl 80000918 <_main>
|
可以看出,SPL的链接地址为0x80000000,即omapl138的SHARERAM中,在0x80000000处程序直接无返回的跳到了reset处,即0x80000060地址。
在reset处,ARM处理器切换到SVC模式,然后跳到\arch\arm\lib\crt0.S的_main处运行。
- 80000918 <_main>:
- 80000918: e59fd054 ldr sp, [pc, #84] ; 80000974 <clbss_l+0x1c>
- 8000091c: e3cdd007 bic sp, sp, #7
- 80000920: e1a0000d mov r0, sp
- 80000924: eb0001a8 bl 80000fcc <board_init_f_alloc_reserve>
- 80000928: e1a0d000 mov sp, r0
- 8000092c: e1a09000 mov r9, r0
- 80000930: eb0001a8 bl 80000fd8 <board_init_f_init_reserve>
- 80000934: e3a00000 mov r0, #0
- 80000938: eb000011 bl 80000984 <board_init_f>
- 8000093c: eb000058 bl 80000aa4 <spl_relocate_stack_gd>
- 80000940: e3500000 cmp r0, #0
- 80000944: 11a0d000 movne sp, r0
- 80000948: 11a09000 movne r9, r0
- 8000094c: e59f0024 ldr r0, [pc, #36] ; 80000978 <clbss_l+0x20>
- 80000950: e59f1024 ldr r1, [pc, #36] ; 8000097c <clbss_l+0x24>
- 80000954: e3a02000 mov r2, #0
|
一开始就设置了栈地址并且8byte对齐,因为如果没有设置栈是不能调用C语言函数的,接下来就可以调用C语言函数啦,跳转到第一个C函数board_init_f_alloc_reserve
- 80000fcc <board_init_f_alloc_reserve>:
- 80000fcc: e24000a8 sub r0, r0, #168 ; 0xa8
- 80000fd0: e3c0000f bic r0, r0, #15
- 80000fd4: e12fff1e bx lr
|
对照C函数
- ulong board_init_f_alloc_reserve(ulong top)
- {
- /* Reserve early malloc arena */
- #if defined(CONFIG_SYS_MALLOC_F)
- top -= CONFIG_SYS_MALLOC_F_LEN;
- #endif
- /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
- top = rounddown(top-sizeof(struct global_data), 16);
-
- return top;
- }
|
- 80000984 <board_init_f>:
- 80000984: e12fff1e bx lr
|
自刚开始设置的sp栈地址处开始往下168Byte空间存放u-boot的global data,函数返回后,继续跳转到board_init_f_init_reserve初始化该数据结构,再次返回后跳转至board_init_f函数:
- 80000aa4 <spl_relocate_stack_gd>:
- 80000aa4: e3a00000 mov r0, #0
- 80000aa8: e12fff1e bx lr
|
该函数什么也没做,直接返回
最后最关键的board_init_r函数,由于该函数篇幅太长,这里不再贴出,该函数完成了omapl138系统时钟的设置,串口控制台的配置等,从启动介质中加载uboot的imag镜像置DDR,然后启动uboot,SPL任务完成。