Uboot連結指令碼與啟動流程

lethe1203發表於2024-03-21
參考資料: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 中。

相關文章