uboot启动流程(5)之board_init_f 函数详解

board_init_f 函数详解

_main 中会调用 board_init_f 函数,board_init_f 函数主要有两个工作:
①、初始化一系列外设,比如串口、定时器,或者打印一些消息等。
②、初始化 gd 的各个成员变量,uboot 会将自己重定位到 DRAM 最后面的地址区域,也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linuxkernel 覆盖掉 uboot,将 DRAM 前面的区域完整的空出来。在拷贝之前肯定要给 uboot 各部分分配好内存位置和大小,比如 gd 应该存放到哪个位置,malloc 内存池应该存放到哪个位置等等。这些信息都保存在 gd 的成员变量中,因此要对 gd 的这些成员变量做初始化。最终形成一个完整的内存“分配图”,在后面重定位 uboot 的时候就会用到这个内存“分配图”。
此函数定义在文件 common/board_f.c 中定义,代码如下:

							示例代码1 board_f.c 代码段
1035 void board_init_f(ulong boot_flags)
1036 {
1037 #ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
1038 /*
1039 * For some archtectures, global data is initialized and used
1040 * before calling this function. The data should be preserved.
1041 * For others, CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined
1042 * and use the stack here to host global data until relocation.
1043 */
1044 gd_t data;
1045
1046 gd = &data;
1047
1048 /*
1049 * Clear global data before it is accessed at debug print
1050 * in initcall_run_list. Otherwise the debug print probably
1051 * get the wrong vaule of gd->have_console.
1052 */
1053 zero_global_data();
1054 #endif
1055
1056 gd->flags = boot_flags;
1057 gd->have_console = 0;
1058
1059 if (initcall_run_list(init_sequence_f))
1060 hang();
1061
1062 #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
1063 !defined(CONFIG_EFI_APP)
1064 /* NOTREACHED - jump_to_copy() does not return */
1065 hang();
1066 #endif
1067 }

因为没有定义CONFIG_SYS_GENERIC_GLOBAL_DATA,所以第1037~1054行代码无效。
第 1056 行,初始化 gd->flags=boot_flags=0。
第 1057 行,设置 gd->have_console=0。
重点在第 1059 行!通过函数 initcall_run_list 来运行初始化序列 init_sequence_f 里面的一些列函数,init_sequence_f 里面包含了一系列的初始化函数,init_sequence_f 也是定义在文件common/board_f.c 中,由于 init_sequence_f 的内容比较长,里面有大量的条件编译代码,这里为了缩小篇幅,将条件编译部分删除掉了,去掉条件编译以后的 init_sequence_f 定义如下:

						示例代码2 board_f.c 代码段
/*****************去掉条件编译语句后的 init_sequence_f***************/
1 static init_fnc_t init_sequence_f[] = {
2 setup_mon_len,
3 initf_malloc,
4 initf_console_record,
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,
10 timer_init, /* initialize timer */
11 board_postclk_init,
12 get_clocks,
13 env_init, /* initialize environment */
14 init_baud_rate, /* initialze baudrate settings */
15 serial_init, /* serial communications setup */
16 console_init_f, /* stage 1 init of console */
17 display_options, /* say that we are here */
18 display_text_info, /* show debugging info if required */
19 print_cpuinfo, /* display cpu info (and speed) */
20 show_board_info,
21 INIT_FUNC_WATCHDOG_INIT
22 INIT_FUNC_WATCHDOG_RESET
23 init_func_i2c,
24 announce_dram_init,
25 /* TODO: unify all these dram functions? */
26 dram_init, /* configure available RAM banks */
27 post_init_f,
28 INIT_FUNC_WATCHDOG_RESET
29 testdram,
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,
46 reserve_mmu,
47 reserve_trace,
48 reserve_uboot,
49 reserve_malloc,
50 reserve_board,
51 setup_machine,
52 reserve_global_data,
53 reserve_fdt,
54 reserve_arch,
55 reserve_stacks, 
56 setup_dram_config,
57 show_dram_config,
58 display_new_sp,
59 INIT_FUNC_WATCHDOG_RESET
60 reloc_fdt,
61 setup_reloc,
62 NULL,
63 };

接下来分析以上函数执行完以后的结果:
第 2 行,setup_mon_len 函数设置 gd 的 mon_len 成员变量,此处为_bss_end -start,也就是整个代码的长度。0X878A8E74-0x87800000=0XA8E74,这个就是代码长度
第 3 行,initf_malloc 函数初始化 gd 中跟 malloc 有关的成员变量,比如 malloc_limit,此函数会设置 gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN=0X400。malloc_limit 表示 malloc内存池大小。
第 4 行 , initf_console_record , 如 果 定 义 了宏 CONFIG_CONSOLE_RECORD 和 宏CONFIG_SYS_MALLOC_F_LEN 的话此函数就会调用函数 console_record_init,但是 IMX6ULL的 uboot 没有定义宏 CONFIG_CONSOLE_RECORD,所以此函数直接返回 0。
第 5 行,arch_cpu_init 函数。
第 6 行,initf_dm 函数,驱动模型的一些初始化。
第 7 行,arch_cpu_init_dm 函数未实现。
第 8 行,mark_bootstage 函数应该是和啥标记有关的
第 9 行,board_early_init_f 函数,板子相关的早期的一些初始化设置,I.MX6ULL 用来初始化串口的 IO 配置
第 10 行,timer_init,初始化定时器,Cortex-A7 内核有一个定时器,这里初始化的就是 Cortex-A 内核的那个定时器。通过这个定时器来为 uboot 提供时间。就跟 Cortex-M 内核 Systick 定时器一样。
第 11 行,board_postclk_init,对于 I.MX6ULL 来说是设置 VDDSOC 电压。
第 12 行,get_clocks 函数用于获取一些时钟值,I.MX6ULL 获取的是 sdhc_clk 时钟,也就
是 SD 卡外设的时钟。
第 13 行,env_init 函数是和环境变量有关的,设置 gd 的成员变量 env_addr,也就是环境变
量的保存地址。
第 14 行,init_baud_rate 函数用于初始化波特率,根据环境变量 baudrate 来初始化 gd->baudrate。
第 15 行,serial_init,初始化串口。
第 16 行,console_init_f,设置 gd->have_console 为 1,表示有个控制台,此函数也将前面暂存在缓冲区中的数据通过控制台打印出来。
第 17 行、display_options,通过串口输出一些信息
第 18 行,display_text_info,打印一些文本信息,如果开启 UBOOT 的 DEBUG 功能的话就会输出 text_base、bss_start、bss_end,形式如下:
debug(“U-Boot code: %08lX -> %08lX BSS: -> %08lX\n”,text_base, bss_start, bss_end);

第 19 行,print_cpuinfo 函数用于打印 CPU 信息

第 20 行,show_board_info 函数用于打印板子信息,会调用 checkboard 函数

第 21 行,INIT_FUNC_WATCHDOG_INIT,初始化看门狗,对于 I.MX6ULL 来说是空函数
第 22 行,INIT_FUNC_WATCHDOG_RESET,复位看门狗,对于 I.MX6ULL 来说是空函数
第 23 行,init_func_i2c 函数用于初始化 I2C

第 24 行,announce_dram_init,此函数很简单,就是输出字符串“DRAM:”
第 26 行,dram_init,并非真正的初始化 DDR,只是设置 gd->ram_size 的值,对于正点原
子 I.MX6ULL 开发板 EMMC 版本核心板来说就是 512MB。
第 27 行,post_init_f,此函数用来完成一些测试,初始化 gd->post_init_f_time
第 29 行,testdram,测试 DRAM,空函数。
第44行,setup_dest_addr函数,设置目的地址,设置gd->ram_size,gd->ram_top,gd->relocaddr这三个的值。接下来我们会遇到很多跟数值有关的设置,如果直接看代码分析的话就太费时间了,我可以修改 uboot 代码,直接将这些值通过串口打印出来,比如这里我们修改文件common/board_f.c,因为setup_dest_addr函数定义在文件common/board_f.c中,在setup_dest_addr函数输入如图所示内容:
uboot启动流程(5)之board_init_f 函数详解_第1张图片
设置好以后重新编译 uboot,然后烧写到 SD 卡中,选择 SD 卡启动,重启开发板,打开
SecureCRT,uboot 会输出下图所示信息:

gd->arch.tlb_size= 0X4000 //MMU 的 TLB 表大小
gd->arch.tlb_addr=0X9FFF0000 //MMU 的 TLB 表起始地址,64KB 对齐以后
gd->relocaddr=0X9FFF0000 //relocaddr 地址

第 47 行,reserve_trace 函数,留出跟踪调试的内存,I.MX6ULL 没有用到!
第 48 行,reserve_uboot, 留出重定位后的 uboot 所占用的内存区域,uboot 所占用大小由
gd->mon_len 所指定,留出 uboot 的空间以后还要对 gd->relocaddr 做 4K 字节对齐,并且重新设置 gd->start_addr_sp,结果如下所示:

gd->mon_len = 0XA8EF4
gd->start_addr_sp = 0X9FF47000
gd->relocaddr = 0X9FF47000

第 49 行,reserve_malloc,留出 malloc 区域,调整 gd->start_addr_sp 位置,malloc 区域由宏TOTAL_MALLOC_LEN 定义,宏定义如下:

#define TOTAL_MALLOC_LEN  (CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)

mx6ull_alientek_emmc.h文件中定义宏CONFIG_SYS_MALLOC_LEN 为16MB=0X1000000,宏CONFIG_ENV_SIZE=8KB=0X2000,因此 TOTAL_MALLOC_LEN=0X1002000。调整以后gd->start_addr_sp 如下所示:

TOTAL_MALLOC_LEN=0X1002000
gd->start_addr_sp=0X9EF45000 //0X9FF47000-16MB-8KB=0X9EF45000

第 50 行,reserve_board 函数,留出板子 bd 所占的内存区,bd 是结构体 bd_t,bd_t 大小为80 字节,结果如下所示:

gd->start_addr_sp=0X9EF44EB8 //0X9EF44FB0-248=0X9EF44EB8
gd->new_gd=0X9EF44EB8

第 53 行,reserve_fdt,留出设备树相关的内存区域,I.MX6ULL 的 uboot 没有用到,因此此函数无效。
第 54 行,reserve_arch 是个空函数。
第 55 行,reserve_stacks,留出栈空间,先对 gd->start_addr_sp 减去 16,然后做 16 字节对齐。如果使能 IRQ 的话还要留出 IRQ 相应的内存,具体工作是由 arch/arm/lib/stack.c 文件中的函数 arch_reserve_stacks 完成。在本 uboot 中并没有使用到 IRQ,所以不会留出 IRQ 相应的内存区域,结果如下所示:

gd->start_addr_sp=0X9EF44E90

第 56 行,setup_dram_config 函数设置 dram 信息,就是设置 gd->bd->bi_dram[0].start 和gd->bd->bi_dram[0].size,后面会传递给 linux 内核,告诉 linux DRAM 的起始地址和大小。结果如下所示:

gd->bd->bi_dram[0].start = 0x80000000
gd->bd->bi_dram[0].size = 0x20000000

可以看出,DRAM 的起始地址为 0X80000000,大小为 0X20000000(512MB)。

第 57 行,show_dram_config 函数,用于显示 DRAM 的配置

第 58 行,display_new_sp 函数,显示新的 sp 位置,也就是 gd->start_addr_sp,不过要定义
宏 DEBUG

第 60 行,reloc_fdt 函数用于重定位 fdt,没有用到。
第 61 行,setup_reloc,设置 gd 的其他一些成员变量,供后面重定位的时候使用,并且将以前的 gd 拷贝到 gd->new_gd 处。需要使能 DEBUG 才能看到相应的信息输出

uboot 重定位后的偏移为 0X18747000,重定位后的新地址为0X9FF4700,新的 gd 首地址为 0X9EF44EB8,最终的 sp 为 0X9EF44E90。至此,board_init_f 函数就执行完成了,最终的内存分配如图

//0X9EF44FB0-248=0X9EF44EB8
gd->new_gd=0X9EF44EB8


第 53 行,reserve_fdt,留出设备树相关的内存区域,I.MX6ULL 的 uboot 没有用到,因此此函数无效。
第 54 行,reserve_arch 是个空函数。
第 55 行,reserve_stacks,留出栈空间,先对 gd->start_addr_sp 减去 16,然后做 16 字节对齐。如果使能 IRQ 的话还要留出 IRQ 相应的内存,具体工作是由 arch/arm/lib/stack.c 文件中的函数 arch_reserve_stacks 完成。在本 uboot 中并没有使用到 IRQ,所以不会留出 IRQ 相应的内存区域,结果如下所示:

```c
gd->start_addr_sp=0X9EF44E90

第 56 行,setup_dram_config 函数设置 dram 信息,就是设置 gd->bd->bi_dram[0].start 和gd->bd->bi_dram[0].size,后面会传递给 linux 内核,告诉 linux DRAM 的起始地址和大小。结果如下所示:

gd->bd->bi_dram[0].start = 0x80000000
gd->bd->bi_dram[0].size = 0x20000000

可以看出,DRAM 的起始地址为 0X80000000,大小为 0X20000000(512MB)。

第 57 行,show_dram_config 函数,用于显示 DRAM 的配置

第 58 行,display_new_sp 函数,显示新的 sp 位置,也就是 gd->start_addr_sp,不过要定义
宏 DEBUG

第 60 行,reloc_fdt 函数用于重定位 fdt,没有用到。
第 61 行,setup_reloc,设置 gd 的其他一些成员变量,供后面重定位的时候使用,并且将以前的 gd 拷贝到 gd->new_gd 处。需要使能 DEBUG 才能看到相应的信息输出

uboot 重定位后的偏移为 0X18747000,重定位后的新地址为0X9FF4700,新的 gd 首地址为 0X9EF44EB8,最终的 sp 为 0X9EF44E90。至此,board_init_f 函数就执行完成了,最终的内存分配如图
uboot启动流程(5)之board_init_f 函数详解_第2张图片

你可能感兴趣的:(U-boot,嵌入式)