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

来源:互联网 发布:海关出口数据怎么查询 编辑:程序博客网 时间:2024/05/23 23:30

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

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

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

目录

  • U-BOOT-201607移植 第一篇 初步分析
  • U-BOOT-201607移植 第二篇 添加单板
  • U-BOOT-201607移植 第三篇 代码重定位
  • 目录
    • 分析board_init_f
      • 1 common board_fc 1035 1067
      • 2 common board_fc 829 1033
      • 3 内存分配图
    • 分析relocate_code
      • 1 archarmlibcrt0S 95 122
      • 2 archarmlibrelocateS 79 130

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
  • 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
  • 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
  • 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 fixlooprelocate_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#endifENDPROC(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
  • 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启动


转载至:http://blog.csdn.net/funkunho/article/details/52474373

0 0