U-BOOT启动流程【02】

前言

第一次学u-boot启动流程的时候我也是一头雾水,当然啦看一遍留个大概印象就好!

要想都学透的话也要花费很大的时间精力

本系列blog分三次写。本节讲的是分析_main函数

在虚拟机中我的Vcode比较卡,所以就在window环境下演示的,不过效果都是相同的,所以无需担心!!!如有错误的地方请及时联系我,我们一起学习!!!

板子是正点原子提供的IMX6ULL系列,所以有些函数例如判断CPU、board类型的时候默认跳过

目录

前言

_main函数详解

 board_init_f函数

relocate_code 函数详解

relocate_vectors 函数详解

board_init_r 函数详解

_main函数详解

_main函数定义在arch/arm/lib/crt0.S 中

   

        76 行:设置sp指针为 CONFIG_SYS_INIT_SP_ADDR,也就是 sp 指向 0X0091FF00
        83 行: sp 做 8 字节对齐。
        85 行:读取 sp 到寄存器 r0 里面,此时 r0=0X0091FF00。
        86 行:调用函数 board_init_f_alloc_reserve

        board_init_f_alloc_reserve函数定义在common/init/board_init.c 中
        board_init_f_alloc_reserve 函数主要是留出早期的malloc 内存区域和 gd 内存区域

        其中CONFIG_SYS_MALLOC_F_LEN=0X400

        sizeof(struct global_data)=248(GD_SIZE 值)

        board_init_f_alloc_reserve函数的返回值为top,保存着预留malloc和gd之后的地址

        top = 0x0091FA00;

        此时的内存分布图为

        87 行:将 r0 写入到SP里,r0保存着函数top返回值,所以此时设置sp = 0x0091FA00

 

         89 行:将 r0 寄存器的值写到寄存器 r9 里面,因为 r9 寄存器存放着全局变量 gd 的地址

        在文件 arch/arm/include/asm/global_data.h 中有此定义。

        uboot 中定义了一个指向 gd_t 的指针 gd, gd 存放在寄存器 r9 里面
的,因此 gd 是个全局变量。 gd_t 是个结构体。

        gd_t在文件 include/asm-generic/global_data.h 里面有定义

         因此 89 行就是设置gd所指向的位置,也就是此时此刻 gd、sp 都指向0X0091FA00这个位置

        90 行:调用函数board_init_f_init_reserve 此函数在文件common/init/board_init.c 中有定义

可以看出,此函数用于初始化 gd,其实就是清零处理。另外,此函数还设置了

gd->malloc_base 为 gd 基地址+gd 大小=0X0091FA00+248=0X0091FAF8,在做 16 字节对齐

最终 gd->malloc_base=0X0091FB00,这个也就是 early malloc 的起始地址。
继续回到示例代码中

        92 行:设置 R0 为 0。
        93 行:调用 board_init_f 函数,主要用来初始化 DDR,定时器,完成代码拷贝等等,此函数我们后面在详细的分析。
        103 行:重新设置环境(sp 和 gd)、获取 gd->start_addr_sp 的值赋给 sp。

        在函数 board_init_f中会初始化 gd 的所有成员变量

        其中 gd->start_addr_sp=0X9EF44E90

        所以这里相当于设置
        sp=gd->start_addr_sp=0X9EF44E90。

        0X9EF44E90 是 DDR 中的地址,说明新的 sp 和 gd 将会存放到 DDR 中,而不是内部的 RAM 了。

        GD_START_ADDR_SP=64

        111 行:获取 gd->bd 的地址赋给 r9,此时 r9 存放的是老的 gd

        这里通过获取 gd->bd 的地址来计算出新的 gd 的位置。

         GD_BD=0。
        112 行:新的 gd 在 bd 下面,所以 r9 减去 gd 的大小就是新的 gd 的位置,获取到新的 gd
的位置以后赋值给 r9。

        114 行:设置 lr 寄存器为 here,这样后面执行其他函数返回的时候就返回到了第 122 行
的 here 位置处。
        115行:读取 gd->reloc_off 的值复制给 r0 寄存器, GD_RELOC_OFF=68
        116行:lr 寄存器的值加上 r0 寄存器的值,重新赋值给 lr 寄存器。

        因为接下来要重定位代码,也就是把代码拷贝到新的地方去(现在的 uboot 存放的起始地址为 0X87800000。下面要将 uboot 拷贝到 DDR 最后面的地址空间处,将 0X87800000 开始的内存空出来),其中就包括here,因此 lr 中的 here 要使用重定位后的位置。
        120 行:读取 gd->relocaddr 的值赋给 r0 寄存器,此时 r0 寄存器就保存着 uboot 要拷贝
的目的地址,为 0X9FF47000。
GD_RELOCADDR=48
        121 行:调用函数 relocate_code,也就是代码重定位函数,此函数负责将 uboot 拷贝到新
的地方去,此函数定义在文件 arch/arm/lib/relocate.S 
        127 行:调用函数 relocate_vectors,对中断向量表做重定位,此函数定义在文件 arch/arm/lib/relocate.S 中。
        131 行:调用函数 c_runtime_cpu_setup,此函数定义在文件arch/arm/cpu/armv7/start.S 中,函数内容如下:

        

        141~159 行:清除 BSS 段。
        167 行:设置函数 board_init_r 的两个参数,函数 board_init_r 声明如下:
                board_init_r(gd_t *id, ulong dest_addr)
                第一个参数是 gd。因此读取 r9 保存到 r0 里面。
                二个参数是目的地址。r1= gd->relocaddr。
        174 行:调用函数 board_init_r
这个就是_main 函数的运行流程

 在此函数里面调用了 board_init_f、 relocate_code、relocate_vectors 和 board_init_r 这 4 个函数

 board_init_f函数

        board_init_f函数有两个重要的工作

        ①、初始化一系列外设,比如串口、定时器、或者打印一些消息

        ②、初始化gd的各个成员变量, uboot 会将自己重定位到 DRAM 最后面的地址区域,也就
是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linux
kernel 覆盖掉 uboot,将 DRAM 前面的区域完整的空出来。在拷贝之前肯定要给 uboot 各部分
分配好内存位置和大小,比如 gd 应该存放到哪个位置, malloc 内存池应该存放到哪个位置等
等。这些信息都保存在 gd 的成员变量中,因此要对 gd 的这些成员变量做初始化。最终形成一
个完整的内存“分配图”,在后面重定位 uboot 的时候就会用到这个内存“分配图”。
        board_init_f函数代码如下

        

board_init_f

        1059 行:进入函数,运行一系列初始化函数

        init_fnc_t init_sequence_f[]函数如下
 

    static init_fnc_t init_sequence_f[] = {
2     setup_mon_len,   //设置 gd 的 mon_len 成员变量,此处为__bss_end -_start,也就
                       //是整个代码的长度。 0X878A8E74-0x87800000=0XA8E74,这个就是代码长度
3     initf_malloc,    //初始化 gd 中跟 malloc 有关的成员变量
4     initf_console_record,    // IMX6ULL的 uboot 没有定义宏 CONFIG_CONSOLE_RECORD,所以此            
                                  函数直接返回 0
5     arch_cpu_init, /* basic arch cpu dependent setup */
6     initf_dm,    //驱动模型的一些初始化
7     arch_cpu_init_dm,
8     mark_bootstage, /* need timer, go after init dm */
9     board_early_init_f,  //板子相关的早期的一些初始化设置I.MX6ULL 用来初始化串口的 IO 配置
10    timer_init, /* initialize timer */    /*初始化定时器*/
11    board_postclk_init,    //设置 VDDSOC 电压
12    get_clocks,    //获取的是 sdhc_clk 时钟,也就是 SD 卡外设的时钟。
13    env_init, /*是和环境变量有关的*/
14    init_baud_rate, /*初始化波特率*/
15    serial_init, /*初始化串口*/
16    console_init_f, /*表示有个控制台*/
17    display_options, /*通过串口输出一些信息*/
18    display_text_info, /*打印一些文本信息*/
19    print_cpuinfo, /*打印 CPU 信息*/
20    show_board_info,/*用于打印板子信息*/
21    INIT_FUNC_WATCHDOG_INIT
22    INIT_FUNC_WATCHDOG_RESET
23    init_func_i2c,/*初始化 I2C*/
24    announce_dram_init,/*输出字符串“DRAM:”*/
25 
26    dram_init, /* configure available RAM banks */
27    post_init_f,
28    INIT_FUNC_WATCHDOG_RESET
29    testdram,/*测试 DRAM*/
30    INIT_FUNC_WATCHDOG_RESET
31    INIT_FUNC_WATCHDOG_RESET
32 /*
33 * Now that we have DRAM mapped and working, we can
34 * relocate the code and continue running from DRAM.
35 *
36 * Reserve memory at end of RAM for (top down in that order):
37 * - area that won't get touched by U-Boot and Linux (optional)
38 * - kernel log buffer
39 * - protected RAM
40 * - LCD framebuffer
41 * - monitor code
42 * - board info struct
43 */
44    setup_dest_addr,
45    reserve_round_4k,/*gd->relocaddr 做 4KB 对 齐*/
46    reserve_mmu,
47    reserve_trace,/*留出跟踪调试的内存*/
48    reserve_uboot,/*留出重定位后的 uboot 所占用的内存区域, uboot 所占用大小由
                      gd->mon_len 所指定,留出 uboot 的空间以后还要对 gd->relocaddr 
                      做 4K 字节对齐,并且重新设置 gd->start_addr_sp*/
49    reserve_malloc,/*留出 malloc 区域*/
50    reserve_board,/*留出板子 bd 所占的内存区, bd 是结构体 bd_t, bd_t 大小为80 字节*/
51    setup_machine,/*设置机器ID*/
52    reserve_global_data,/*保留出 gd_t 的内存区域*/
53    reserve_fdt,/*留出设备树相关的内存区域*/
54    reserve_arch,
55    reserve_stacks,/*留出栈空间*/


56    setup_dram_config,/*告诉 linux DRAM 的起始地址和大小*/
57    show_dram_config,/*用于显示 DRAM 的配置*/
58    display_new_sp,
59    INIT_FUNC_WATCHDOG_RESET
60    reloc_fdt,
61    setup_reloc,
62    NULL,
63 };

最终的内存分配图

relocate_code 函数详解

relocate_code 函数是用于代码拷贝的
 

relocate_code

relocate_code

        80 行:r1=__image_copy_start,也就是 r1 寄存器保存源地址
                        __image_copy_start=0X87800000。
        81 行:r0=0X9FF47000,这个地址就是 uboot 拷贝的目标首地址。

                        r4=r0-r1=0X9FF47000-0X87800000=0X18747000,因此 r4 保存偏移量。
        82 行:如果在第 81 中,r0-r1 等于 0,说明 r0 和 r1 相等,也就是源地址和目的地址是
一样的,那肯定就不需要拷贝了!执行 relocate_done 函数
        83 行:r2=__image_copy_end, r2 中保存拷贝之前的代码结束地址
                        __image_copy_end =0x8785dd54。
        84 行:函数 copy_loop 完成代码拷贝工作

        从 r1,也就是__image_copy_start 开始,读取 uboot 代码保存到 r10 和 r11 中,一次就只拷贝这 2 个 32 位的数据。拷贝完成以后 r1 的值会更新,保存下一个要拷贝的数据地址。
        87 行:将 r10 和 r11 的数据写到 r0 开始的地方,也就是目的地址。写完以后 r0 的值会
更新,更新为下一个要写入的数据地址。
        88 行:比较 r1 是否和 r2 相等,也就是检查是否拷贝完成,如果不相等的话说明没有拷
贝完成, 没有拷贝完成的话就跳转到 copy_loop 接着拷贝,直至拷贝完成。
        94 行~109 行:是重定位.rel.dyn 段。.rel.dyn 段是存放.text 段中需要重定位地址的集合。重定位就是 uboot 将自身拷贝到 DRAM 的另一个地放去继续运行(DRAM 的高地址处)。一个可执行的 bin 文件,其链接地址和运行地址要相等,也就是链接到哪个地址。在运行之前就要拷贝到哪个地址去。

relocate_vectors 函数详解

函数 relocate_vectors 用于重定位向量表
此函数定义在文件 relocate.S 中。函数如下:

relocate_vectors

board_init_r 函数详解

board_init_f 并没有初始化所有的外设,还需要做一些后续工作

这些后续工作就是由函数 board_init_r来完成的

board_init_r

         1016行:又看到了熟悉的函数,在上面讲述的board_init_f中也有类似的,我们来分析一下

init_fnc_t init_sequence_r[] = {
02     initr_trace,    /*如果定义了宏 CONFIG_TRACE 的话就会调用函数 trace_init,
                        初始化和调试跟踪有关的内容。*/
03     initr_reloc,    /*函数用于设置 gd->flags,标记重定位完成。*/
04     initr_caches,   /*初始化 cache*/
05     initr_reloc_global_data,/*初始化重定位后 gd 的一些成员变量*/
06     initr_barrier,
07     initr_malloc,    /*初始化malloc*/
08     initr_console_record,
09     bootstage_relocate,/*启动状态重定位*/
10     initr_bootstage,    /*初始化 bootstage 什么的*/
11     board_init, /* 板级初始化 */
12     stdio_init_tables,/* stdio 相关初始化*/
13     initr_serial,    /*初始化串口*/
14     initr_announce,    /*与调试有关,通知已经在 RAM 中运行。*/
15     INIT_FUNC_WATCHDOG_RESET
16     INIT_FUNC_WATCHDOG_RESET
17     INIT_FUNC_WATCHDOG_RESET
18     power_init_board,
19     initr_flash,
20     INIT_FUNC_WATCHDOG_RESET
21     initr_nand,
22     initr_mmc,    /*初始化 EMMC*/
23     initr_env,    /*初始化环境变量*/
24     INIT_FUNC_WATCHDOG_RESET
25     initr_secondary_cpu,    /*初始化其他 CPU 核*/
26     INIT_FUNC_WATCHDOG_RESET
27     stdio_add_devices,    /*各种输入输出设备的初始化*/
28     initr_jumptable,    /*初始化跳转表*/
29     console_init_r, /* 控 制 台 初 始 化 */
30     INIT_FUNC_WATCHDOG_RESET
31     interrupt_init,    /*初始化中断*/
32     initr_enable_interrupts,/*使能中断*/
33     initr_ethaddr,    /*初始化网络地址*/
34     board_late_init,    /*板子后续初始化*/
35     INIT_FUNC_WATCHDOG_RESET
36     INIT_FUNC_WATCHDOG_RESET
37     INIT_FUNC_WATCHDOG_RESET
38     initr_net,
39     INIT_FUNC_WATCHDOG_RESET
40     run_main_loop,
41 };

        最后跳转到run_main_loop 行,主循环,处理命令

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值