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,然后烧写到 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 函数就执行完成了,最终的内存分配如图
在这里插入图片描述

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
uboot是一种常用的开源引导加载程序,用于嵌入式系统的引导启动。其中,board_init_r和board_init_f是uboot中的两个重要函数board_init_r函数是在uboot启动过程中执行的第一个函数。它负责执行一系列的初始化工作,例如初始化系统时钟、设置内存映射等。此函数被用于配置和初始化各个硬件模块,包括中断控制器、串口控制器、定时器等,以确保系统正常运行。该函数还读取并解析配置文件,加载设备树等操作,为后续的引导加载准备好必要的条件。 board_init_f函数是在board_init_r函数之后调用的。它用于进一步初始化系统,并执行一些与硬件相关的操作。例如,该函数可能会初始化网络接口、USB接口、存储设备等,并设置系统的默认环境变量。此外,board_init_f函数还负责将uboot的控制权交给操作系统的引导加载程序,从而完成uboot的使命。 通过调用board_init_r和board_init_f函数,uboot能够在系统启动时完成各种硬件的初始化和配置工作。这两个函数是uboot启动过程中的重要环节,确保系统能够顺利地加载操作系统并运行。同时,它们也为开发者提供了扩展uboot的接口,可以在这两个函数中添加自定义的初始化代码,以满足系统特定的需求。 总结来说,board_init_r和board_init_f是uboot中两个重要的函数,用于初始化和配置嵌入式系统的硬件,并为操作系统的加载做好准备。它们是uboot启动过程中不可或缺的一部分,保证系统的正常启动和运行。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值