uboot源码分析七 uboot启动流程四 board_init_f 函数(板子初始化内存空间分配)

_main 中会 board_init_f 函数, board_init_f 函数主要有两个工作:
①、初始化一系列外设,比如串口、定时器,或者打印一些消息等。
②、初始化 gd 的各个成员变量, uboot 会将自己重定位到 DRAM 最后面的地址区域,也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linux kernel 覆盖掉 uboot,将 DRAM 前面的区域完整的空出来。在拷贝之前肯定要给 uboot 各部分分配好内存位置和大小,比如 gd 应该存放到哪个位置, malloc 内存池应该存放到哪个位置等等。这些信息都保存在 gd 的成员变量中,因此要对 gd 的这些成员变量做初始化。最终形成一个完整的内存“分配图”,在后面重定位 uboot 的时候就会用到这个内存“分配图”。
uboot源码分析七 uboot启动流程四 board_init_f 函数(板子初始化内存空间分配)_第1张图片
uboot源码分析七 uboot启动流程四 board_init_f 函数(板子初始化内存空间分配)_第2张图片

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))
		hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
		!defined(CONFIG_EFI_APP)
	/* NOTREACHED - jump_to_copy() does not return */
	hang();
#endif

	/* Light up LED1 */
	imx6_light_up_led1();
}

为没有定义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 定义如下:

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 };

uboot源码分析七 uboot启动流程四 board_init_f 函数(板子初始化内存空间分配)_第3张图片
setup_mon_len 函数设置 gd 的 mon_len 成员变量,此处为__bss_end -_start,也就是整个代码的长度。 0X878A8E74-0x87800000=0XA8E74,这个就是代码长度 u-boot 编译出来每一次都不同。

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 函数,初始化架构相关的内容, CPU 级别的操作。
第 6 行, initf_dm 函数,驱动模型的一些初始化。
第 7 行, arch_cpu_init_dm 函数未实现。
第 8 行, mark_bootstage 函数应该是标记启动阶段有关的
第 9 行, board_early_init_f 函数,板子相关的早期的一些初始化设置, I.MX6ULL 用来初始化串口的 IO 配置
第 10 行, timer_init,初始化定时器, Cortex-A7 内核有一个定时器,这里初始化的就是 CortexA 内核的那个定时器。通过这个定时器来为uboot 提供时间。就跟 Cortex-M 内核 Systick 定时器一样。关于 Cortex-A 内部定时器的详细内容,请参考文档《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》 的“Chapter B8 The Generic Timer”章节。
第 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);
在include/configs 板子.h 加上#define DEBUG

第 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 的值
第 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 中
第 45 行 , reserve_round_4k 函 数 用 于 对 gd->relocaddr 做 4KB 对 齐 , 因 为gd->relocaddr=0XA0000000,已经是 4K 对齐了,所以调整后不变。
第 46 行, reserve_mmu,留出 MMU 的 TLB 表的位置,分配 MMU 的 TLB 表内存以后会对 gd->relocaddr 做 64K 字节对齐。完成以后 gd->arch.tlb_size、 gd->arch.tlb_addr 和 gd->relocaddr
第 47 行, reserve_trace 函数,留出跟踪调试的内存, I.MX6ULL 没有用到!
第 48 行, reserve_uboot, 留出重定位后的 uboot 所占用的内存区域, uboot 所占用大小由gd->mon_len 所指定,留出 uboot 的空间以后还要对 gd->relocaddr 做 4K 字节对齐,并且重新设置 gd->start_addr_sp
第 49 行, reserve_malloc,留出 malloc 区域,调整 gd->start_addr_sp 位置, malloc 区域由宏TOTAL_MALLOC_LEN 定义
宏 CONFIG_SYS_MALLOC_LEN 为 16MB=0X1000000,宏 CONFIG_ENV_SIZE=8KB=0X2000,因此 、TOTAL_MALLOC_LEN=0X1002000。调整以后gd->start_addr_sp
第 50 行, reserve_board 函数,留出板子 bd 所占的内存区, bd 是结构体 bd_t, bd_t 大小为80 字节
第 51 行, setup_machine,设置机器 ID, linux 启动的时候会和这个机器 ID 匹配,如果匹配的话 linux 就会启动正常。但是!! I.MX6ULL 不用这种方式了,这是以前老版本的 uboot 和linux 使用的,新版本使用设备树了, 因此此函数无效。
第 52 行, reserve_global_data 函数,保留出 gd_t 的内存区域, gd_t 结构体大小为 248B
第 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 完成。
第 56 行, setup_dram_config 函数设置 dram 信息,就是设置 gd->bd->bi_dram[0].start 和gd->bd->bi_dram[0].size,后面会传递给 linux 内核,告诉 linux DRAM 的起始地址和大小。
第 57 行, show_dram_config 函数,用于显示 DRAM 的配置
第 58 行, display_new_sp 函数,显示新的 sp 位置,也就是 gd->start_addr_sp,不过要定义宏 DEBUG
第 60 行, reloc_fdt 函数用于重定位,没有用到。
第 61 行, setup_reloc,设置 gd 的其他一些成员变量,供后面重定位的时候使用,并且将以前的 gd 拷贝到 gd->new_gd 处。需要使能 DEBUG 才能看到相应的信息输出

在这里插入图片描述
uboot 重定位后的偏移为 0X18747000,重定位后的新地址为0X9FF4700,新的 gd 首地址为 0X9EF44EB8,最终的 sp 为 0X9EF44E90。

你可能感兴趣的:(linux,uboot,uboot)