U-BOOT-2016.07移植 (第三篇) 代码重定位

U-BOOT-2016.07移植 (第一篇) 初步分析

U-BOOT-2016.07移植 (第二篇) 添加单板

U-BOOT-2016.07移植 (第三篇) 代码重定位

目录

原文链接:http://blog.csdn.net/funkunho/article/details/52474373?utm_source=itdadao&utm_medium=referral

1. 分析board_init_f()

在我写的第一篇文章中,已经对u-boot-2016.07的启动流程有了初步的了解,现在我们开始分析crt0.S中,_main函数在设置好栈和GD后调用的board_init_f(),从而了解u-boot是如何对内存空间进行分配,然后进行重定位的。

1.1 common/ board_f.c (1035 ~ 1067):

void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA   //没有定义这个宏,不关心
    /*
     * For some archtectures, global data is initialized and used before
     * calling this function. The data should be preserved. For others,
     * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
     * here to host global data until relocation.
     */
    gd_t data;

    gd = &data;

    /*
     * Clear global data before it is accessed at debug print
     * in initcall_run_list. Otherwise the debug print probably
     * get the wrong vaule of gd->have_console.
     */
    zero_global_data();
#endif

    gd->flags = boot_flags;
    gd->have_console = 0;

    if (initcall_run_list(init_sequence_f))   //调用initcall_run_list函数,
                                              //这个函数在lib/initcall.c中定义,
                                              //作用就是调用init_sequence_f函数数组中
                                              //存放的各初始化函数
        hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \   //我们有定义CONFIG_ARM,不关心
        !defined(CONFIG_EFI_APP)
    /* NOTREACHED - jump_to_copy() does not return */
    hang();
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

下面我们分析init_sequence_f数组中的各函数

1.2 common/ board_f.c (829 ~ 1033):

这里我直接将被宏开关关掉的函数剔除掉,留下最终会被调用的函数:

static init_fnc_t init_sequence_f[] = {
    setup_mon_len,      //gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
                        //CONFIG_SYS_MONITOR_BASE = _start = 0
                        //设置gd->mon_len为编译出来的u-boot.bin+bss段的大小
    arch_cpu_init,      /* basic arch cpu dependent setup */
                        //这个函数应该是留给移植人员使用的,里面什么都没做,而且被__weak修饰,
                        //所以我们可以在别的地方重新定义这个函数来取代它
    arch_cpu_init_dm,   //同上
    mark_bootstage,     /* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
    board_early_init_f,    //在smdk2440.c中定义,初始化CPU时钟和各种IO(待修改)
#endif
    /* TODO: can any of this go into arch_cpu_init()? */
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
        defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
        defined(CONFIG_SPARC)
    timer_init,     /* 初始化定时器 */   
#endif
    env_init,       /* 初始化环境变量 */  
    init_baud_rate,     /* 初始化波特率为: 115200 */
    serial_init,        /* 设置串口通讯 */
    console_init_f,     /* stage 1 init of console */

    display_options,    /* 打印版本信息,你可以修改include/version.h中的CONFIG_IDENT_STRING选项,
                         * 加入你的身份信息 
                         */
    display_text_info,  /* show debugging info if required */ //打印bss段信息及text_base, 
                                                              //需要 #define DEBUG
    print_cpuinfo,      /* 打印CPUID和时钟频率 */
    INIT_FUNC_WATCHDOG_INIT
    INIT_FUNC_WATCHDOG_RESET
    announce_dram_init,        //输出"DRAM: " 然后在下面进行SDRAM参数设置
    /* TODO: unify all these dram functions? */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
        defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
    dram_init,      /* 在smdk2440.c中定义,配置SDRAM大小,大家可根据实际进行修改 */
#endif
    INIT_FUNC_WATCHDOG_RESET
    INIT_FUNC_WATCHDOG_RESET
    INIT_FUNC_WATCHDOG_RESET
    /*
     * Now that we have DRAM mapped and working, we can
     * relocate the code and continue running from DRAM.
     *
     * Reserve memory at end of RAM for (top down in that order):
     *  - area that won't get touched by U-Boot and Linux (optional)
     *  - kernel log buffer
     *  - protected RAM
     *  - LCD framebuffer
     *  - monitor code
     *  - board info struct
     */
    setup_dest_addr,     //将gd->relocaddr、gd->ram_top指向SDRAM最顶端
    reserve_round_4k,    //gd->relocaddr 4K对齐
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
        defined(CONFIG_ARM)
    reserve_mmu,      //gd->arch.tlb_size = PGTABLE_SIZE; 预留16kb的MMU页表
                      //gd->relocaddr -= gd->arch.tlb_size;
                      //gd->relocaddr &= ~(0x10000 - 1); 64kb对齐
                      //gd->arch.tlb_addr = gd->relocaddr;
#endif
    reserve_trace,
#if !defined(CONFIG_BLACKFIN)
    reserve_uboot,    //gd->relocaddr -= gd->mon_len;    一开始设置的u-boot.bin + bss段长度
                      //gd->relocaddr &= ~(4096 - 1);    4k对齐,这是最终重定位地址
                      //gd->start_addr_sp = gd->relocaddr;  设置重定位后的栈指针
#endif
#ifndef CONFIG_SPL_BUILD
    reserve_malloc,   //gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; 
                      //预留4MB MALLOC内存池
    reserve_board,    //gd->start_addr_sp -= sizeof(bd_t);  预留空间给重定位后的gd_t->bd
                      //gd->bd = (bd_t *)gd->start_addr_sp; 指定重定位bd地址
                      //memset(gd->bd, '\0', sizeof(bd_t)); 清零
#endif
    setup_machine,    //gd->bd->bi_arch_number = CONFIG_MACH_TYPE; 
                      //对于S3C2440来说就是MACH_TYPE_S3C2440 (arch/arm/include/asm/mach-types.h)
    reserve_global_data,    //gd->start_addr_sp -= sizeof(gd_t);
                            //gd->new_gd = (gd_t *)gd->start_addr_sp; 指定重定位GD地址
    reserve_fdt,
    reserve_arch,
    reserve_stacks,         //gd->start_addr_sp -= 16;   栈指针16字节对齐
                            //gd->start_addr_sp &= ~0xf;  
    setup_dram_config,      //gd->bd->bi_dram[i].start = addr;   设置sdram地址和大小
                            //gd->bd->bi_dram[i].size = size;
    show_dram_config,       //打印SDRAM大小,与上面的announce_dram_init相对应
    display_new_sp,         //若 #define DEBUG 则打印新的栈地址
    INIT_FUNC_WATCHDOG_RESET
    reloc_fdt,
    setup_reloc,    //gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; 计算重定位偏移地址
                    //memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); 
                    //将原来的gd复制到重定位后的gd地址上去
    NULL,
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

1.3 内存分配图

根据1.1、1.2的分析,可以得出以下的内存分配图:
U-BOOT重定位内存分配图

2. 分析relocate_code

2.1 arch/arm/lib/crt0.S (95 ~ 122)

同样,这里将无关紧要的宏开关去掉

    ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
    bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
    ldr r9, [r9, #GD_BD]        /* r9 = gd->bd */
    sub r9, r9, #GD_SIZE        /* new GD is below bd */

    /*
     * 上面这一段代码是将board_init_f中设置好的start_addr_sp地址值赋给栈指针,使其指向重定位后的栈顶
     * 8字节对齐后,将r9设为新的GD地址(对照内存分配图: gd地址=bd地址-sizeof(gd_t))
     */

    adr lr, here           //设置返回地址为下面的here,重定位到sdram后返回here运行
    ldr r0, [r9, #GD_RELOC_OFF]     /* r0 = gd->reloc_off 取重定位地址偏移值 */
    add lr, lr, r0                  //返回地址加偏移地址等于重定位后在sdram中的here地址
    ldr r0, [r9, #GD_RELOCADDR]     /* r0 = gd->relocaddr 传入参数为重定位地址 */
    b   relocate_code               //跳到arch/arm/lib/relocate.S中执行
here:                               //返回后跳到sdram中运行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.2 arch/arm/lib/relocate.S (79 ~ 130)

ENTRY(relocate_code)
    ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start 这是u-boot.bin起始链接地址,
                                 * 定义在u-boot.lds中 (编译后在顶层目录生成)
                                 * 原文件是arch/arm/cpu/u-boot.lds,大家可以自行分析
                                 */

    subs    r4, r0, r1      /* r4 <- relocation offset r0是crt0.S中传入的重定位地址,
                             * 这里是算出偏移值 
                             */

    beq relocate_done       /* skip relocation 如果r4为0,则认为重定位已完成 */
    ldr r2, =__image_copy_end   /* r2 <- SRC &__image_copy_end 同第一条指令,在u-boot.lds中定义*/

copy_loop:      /* r1是源地址__image_copy_start,r0是目的地址relocaddr,
                 * size = __image_copy_start - __image_copy_end 
                 */
    ldmia   r1!, {r10-r11}      /* copy from source address [r1]    */
    stmia   r0!, {r10-r11}      /* copy to   target address [r0]    */
    cmp r1, r2          /* until source end address [r2]    */
    blo copy_loop

    /*
     * fix .rel.dyn relocations        定义了"-PIE"选项就会执行下面这段代码
     * 目的是为了让位置相关的资源(代码、参数、变量)的地址在重定位后仍然能被寻址到,所以让他们加上偏移地址,
     * 即等于他们重定位后的真正地址
     * 这些 "存放(资源的地址)的地址" 存放在.rel.dyn这个段中,每个参数后面都会跟着一个起标志作用的参数,
     * 如果这个标志参数为23,即0x17,则表示这个 (资源的地址) 是位置相关的,需要加上重定位偏移值
     * 这一段代码首先让.rel.dyn这个段中的存放的地址值加上偏移值,使其在sdram中取出(资源的地址)
     * 然后再让这些(资源的地址)加上偏移值,存回rel.dyn中存放这些地址的地址中,
     * 比较拗口,抽象,大家多研究研究代码,或看看我下面发的图来帮助理解
     */
    ldr r2, =__rel_dyn_start    /* r2 <- SRC &__rel_dyn_start */
    ldr r3, =__rel_dyn_end  /* r3 <- SRC &__rel_dyn_end */
fixloop:
    ldmia   r2!, {r0-r1}        /* (r0,r1) <- (SRC location,fixup) r0为"存放(资源的地址)的地址",
                                 * 这个地址里存放的是需要用到的(资源的地址),r1为标志值
                                 */
    and r1, r1, #0xff    //r1取低八位
    cmp r1, #23          /* relative fixup? 和23比较,如果相等则继续往下,否则跳到fixnext */
    bne fixnext

    /* relative fix: increase location by offset */
    add r0, r0, r4      //r4存放的是重定位偏移值,r0这个地址存放的是位置相关的(资源的地址),
                        //r4+r0即为重定位后的"存放(资源的地址)的地址",
    ldr r1, [r0]        //在sdram中取出还未修改的(资源的地址)
    add r1, r1, r4      //加上偏移值
    str r1, [r0]        //存回去
fixnext:                //跳到下一个继续检测是否需要重定位
    cmp r2, r3
    blo fixloop

relocate_done:

    /* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__   
    mov pc, lr                 //ARM920T用的汇编指令集是ARMv4,所以使用这条返回指令,
                               //返回重定位后的here标志
#else
    bx  lr
#endif

ENDPROC(relocate_code)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 原谅博主表达能力太渣,我发一下图吧
    u-boot代码重定位

到这里,代码重定位的分析就结束了,下一篇内容将开始修改代码使u-boot能从nor flash启动

发布了17 篇原创文章 · 获赞 3 · 访问量 3553

猜你喜欢

转载自blog.csdn.net/sgy1993/article/details/78256649