參考資料:https://www.bilibili.com/video/BV12E411h71h?p=21&vd_source=432ba293ecfc949a4174ab91ccc526d6
imx6ull uboot連結指令碼為u-boot.lds,對連結指令碼進行簡要分析:
u-boot.lds
1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 2 OUTPUT_ARCH(arm) 3 ENTRY(_start) // 入口函式,該函式在verctor.S中定義 4 SECTIONS 5 { 6 . = 0x00000000; 7 . = ALIGN(4); 8 .text : // 程式碼段 9 { 10 *(.__image_copy_start) // 0x87800000,整個映象的起始地址,複製起始地址 11 *(.vectors) // 0x87800000,存放中斷向量表 12 arch/arm/cpu/armv7/start.o (.text*) // start.c 13 *(.text*) // .text段結束 14 } 15 . = ALIGN(4); 16 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 17 . = ALIGN(4); 18 .data : { 19 *(.data*) 20 } 21 . = ALIGN(4); 22 . = .; 23 . = ALIGN(4); 24 .u_boot_list : { 25 KEEP(*(SORT(.u_boot_list*))); 26 } 27 . = ALIGN(4); 28 .image_copy_end : // 映象複製結束,0x8784f1a4 29 { 30 *(.__image_copy_end) 31 } 32 .rel_dyn_start : // rel段起始地址0x8784f1a4 33 { 34 *(.__rel_dyn_start) 35 } 36 .rel.dyn : { 37 *(.rel*) 38 } 39 .rel_dyn_end : // rel段結束地址0x8785794c 40 { 41 *(.__rel_dyn_end) 42 } 43 .end : 44 { 45 *(.__end) 46 } 47 _image_binary_end = .; // 0x8785794c 48 . = ALIGN(4096); 49 .mmutable : { 50 *(.mmutable) 51 } 52 .bss_start __rel_dyn_start (OVERLAY) : { // bss段起始地址:0x8784f1a4 53 KEEP(*(.__bss_start)); 54 __bss_base = .; 55 } 56 .bss __bss_base (OVERLAY) : { 57 *(.bss*) 58 . = ALIGN(4); 59 __bss_limit = .; 60 } 61 .bss_end __bss_limit (OVERLAY) : { // bss段結束地址0x8789a194 62 KEEP(*(.__bss_end)); 63 } 64 .dynsym _image_binary_end : { *(.dynsym) } 65 .dynbss : { *(.dynbss) } 66 .dynstr : { *(.dynstr*) } 67 .dynamic : { *(.dynamic*) } 68 .plt : { *(.plt*) } 69 .interp : { *(.interp*) } 70 .gnu.hash : { *(.gnu.hash) } 71 .gnu : { *(.gnu*) } 72 .ARM.exidx : { *(.ARM.exidx*) } 73 .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
vector.S
_start: #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG .word CONFIG_SYS_DV_NOR_BOOT_CFG #endif // 中斷向量表 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
在u-boot.map中有記憶體配置部分:
Name Origin Length Attributes *default* 0x0000000000000000 0xffffffffffffffff Linker script and memory map Address of section .text set to 0x87800000 0x0000000000000000 . = 0x0 0x0000000000000000 . = ALIGN (0x4) .text 0x0000000087800000 0x3cd64 *(.__image_copy_start) .__image_copy_start 0x0000000087800000 0x0 arch/arm/lib/built-in.o 0x0000000087800000 __image_copy_start *(.vectors) .vectors 0x0000000087800000 0x300 arch/arm/lib/built-in.o 0x0000000087800000 _start 0x0000000087800020 _undefined_instruction 0x0000000087800024 _software_interrupt 0x0000000087800028 _prefetch_abort 0x000000008780002c _data_abort 0x0000000087800030 _not_used 0x0000000087800034 _irq 0x0000000087800038 _fiq 0x0000000087800040 IRQ_STACK_START_IN arch/arm/cpu/armv7/start.o(.text*) .text 0x0000000087800300 0xb0 arch/arm/cpu/armv7/start.o 0x0000000087800300 reset 0x0000000087800304 save_boot_params_ret 0x0000000087800340 c_runtime_cpu_setup 0x0000000087800350 save_boot_params 0x0000000087800354 cpu_init_cp15 0x00000000878003a8 cpu_init_crit
函式執行流程:
reset ->save_boot_params ->save_boot_params_ret // 將處理器切成svc模式,並關閉FIQ和IRQ,設定中斷向量偏移 ->cpu_init_cp15 // 設定cp15協處理器 ->cpu_init_crit ->lowlevel_init // 設定sp指標,r9暫存器 ->s_init(空函式) ->_main
save_boot_params_ret函式作用:將處理器切成svc模式,並關閉FIQ和IRQ,設定中斷向量偏移
mrs r0, cpsr and r1, r0, #0x1f @ mask mode bits teq r1, #0x1a @ test for HYP mode bicne r0, r0, #0x1f @ clear all mode bits orrne r0, r0, #0x13 @ set SVC mode orr r0, r0, #0xc0 @ disable FIQ and IRQ msr cpsr,r0 mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register bic r0, #CR_V @ V = 0,將V清零可以重定位中斷向量表 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register /* Set vector address in CP15 VBAR register */ ldr r0, =_start // 程式碼起始的地址,也是中斷向量表起始的地方 mcr p15, 0, r0, c12, c0, 0 @Set VBAR #endif /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 bl cpu_init_crit
lowlevel_init.S
ldr r0, =_start // 程式碼起始的地址,也是中斷向量表起始的地方 mcr p15, 0, r0, c12, c0, 0 @Set VBAR #endif /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 bl cpu_init_crit lowlevel_init.S ENTRY(lowlevel_init) /* * Setup a temporary stack. Global data is not available yet. */ ldr sp, =CONFIG_SYS_INIT_SP_ADDR // 先讓sp指標指向內部ram,0x91ff00,imx6ull內部ram地址為0x900000-0x91ffff bic sp, sp, #7 /* 8-byte alignment for ABI compliance,八位元組對齊 */ #ifdef CONFIG_SPL_DM mov r9, #0 #else /* * Set up global data for boards that still need it. This will be * removed soon. */ #ifdef CONFIG_SPL_BUILD ldr r9, =gdata #else sub sp, sp, #GD_SIZE bic sp, sp, #7 mov r9, sp // 0x91fe08 #endif #endif /* * Save the old lr(passed in ip) and the current lr to stack */ push {ip, lr} /* * Call the very early init function. This should do only the * absolute bare minimum to get started. It should not: * * - set up DRAM * - use global_data * - clear BSS * - try to start a console * * For boards with SPL this should be empty since SPL can do all of * this init in the SPL board_init_f() function which is called * immediately after this. */ bl s_init pop {ip, pc} ENDPROC(lowlevel_init)
crt0.S
ENTRY(_main) /* * Set up initial C runtime environment and call board_init_f(0). */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr sp, =(CONFIG_SPL_STACK) #else ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) #endif #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif mov r0, sp bl board_init_f_alloc_reserve // 傳入r0引數,也就是sp指標 mov sp, r0 /* set up gd here, outside any C code */ mov r9, r0 // r9儲存的就是gd指標,也就是 gd 指向 0X0091FA00 bl board_init_f_init_reserve mov r0, #0 bl board_init_f // 初始化一系列外設,初始化gd的各個變數 ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ sub r9, r9, #GD_SIZE /* new GD is below bd */ adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ add lr, lr, r0 #if defined(CONFIG_CPU_V7M) orr lr, #1 /* As required by Thumb-only */ #endif ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ b relocate_code // 重定位程式碼 here: /* * now relocate vectors */ bl relocate_vectors // 重定位中斷向量表 /* Set up final (full) environment */ bl c_runtime_cpu_setup /* we still call old routine here */ #endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK) # ifdef CONFIG_SPL_BUILD /* Use a DRAM stack for the rest of SPL, if requested */ bl spl_relocate_stack_gd cmp r0, #0 movne sp, r0 movne r9, r0 # endif ldr r0, =__bss_start /* this is auto-relocated! */ #ifdef CONFIG_USE_ARCH_MEMSET ldr r3, =__bss_end /* this is auto-relocated! */ mov r1, #0x00000000 /* prepare zero to clear BSS */ subs r2, r3, r0 /* r2 = memset len */ bl memset #else ldr r1, =__bss_end /* this is auto-relocated! */ mov r2, #0x00000000 /* prepare zero to clear BSS */ clbss_l:cmp r0, r1 /* while not at end of BSS */ #if defined(CONFIG_CPU_V7M) itt lo #endif strlo r2, [r0] /* clear 32-bit BSS word */ addlo r0, r0, #4 /* move to next */ blo clbss_l #endif #if ! defined(CONFIG_SPL_BUILD) bl coloured_LED_init bl red_led_on #endif /* call board_init_r(gd_t *id, ulong dest_addr) */ mov r0, r9 /* gd_t */ ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */ /* call board_init_r */ #if defined(CONFIG_SYS_THUMB_BUILD) ldr lr, =board_init_r /* this is auto-relocated! */ // 程式碼重定位和初始化一些外設 bx lr #else ldr pc, =board_init_r /* this is auto-relocated! */ #endif /* we should not return here. */ #endif ENDPROC(_main) ulong board_init_f_alloc_reserve(ulong top) { /* Reserve early malloc arena */ #if defined(CONFIG_SYS_MALLOC_F) top -= CONFIG_SYS_MALLOC_F_LEN; // CONFIG_SYS_MALLOC_F_LEN定義的也就是uboot早期的malloc記憶體池的大小,sp減去400位元組大小 #endif /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */ top = rounddown(top-sizeof(struct global_data), 16); return top; } void board_init_f_init_reserve(ulong base) { struct global_data *gd_ptr; #ifndef _USE_MEMCPY int *ptr; #endif /* * clear GD entirely and set it up. * Use gd_ptr, as gd may not be properly set yet. */ gd_ptr = (struct global_data *)base; /* zero the area */ #ifdef _USE_MEMCPY memset(gd_ptr, '\0', sizeof(*gd)); #else for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); ) *ptr++ = 0; #endif /* set GD unless architecture did it already */ #if !defined(CONFIG_ARM) arch_setup_gd(gd_ptr); #endif /* next alloc will be higher by one GD plus 16-byte alignment */ base += roundup(sizeof(struct global_data), 16); /* * record early malloc arena start. * Use gd as it is now properly set for all architectures. */ #if defined(CONFIG_SYS_MALLOC_F) /* go down one 'early malloc arena' */ gd->malloc_base = base; // malloc_base = 0X0091FB00 /* next alloc will be higher by one 'early malloc arena' size */ base += CONFIG_SYS_MALLOC_F_LEN; #endif }
在imx6ull記憶體佈局中,也就是在典型的記憶體佈局中,通常會將 .rodata 段放在 .text 段之後,而 bss 段則會放在 .rodata 段之後。簡單來說,記憶體佈局的順序通常是 .text -> .rodata -> bss,所以通常情況下,計算程式碼長度時可以使用__bss_end - _start這種方法
uboot 啟動以後會進入 3 秒倒數計時,如果在 3 秒倒數計時結束之前按下按下Enter鍵,那麼就會進入 uboot 的命令模式,如果倒數計時結束以後都沒有按下Enter鍵,那麼就會自動啟動 Linux 核心 , 這 個 功 能 就 是 由 run_main_loop 函 數 來 完 成 的
cli_loop 函式是 uboot 的命令列處理函式,我們在 uboot 中輸入各種命令,進行各種操作就是有 cli_loop 來處理的,此函式定義在檔案 common/cli.c 中。