一. uboot启动流程
_main 函数中会调用 board_init_f 函数,本文简单分析一下 board_init_f 函数。
二. board_init_f 函数
board_init_f 函数主要有两个工作:
(1) 初始化一系列外设,比如串口、定时器,或者打印一些消息等。
(2) 初始化 gd 的各个成员变量,uboot 会将自己重定位到 DRAM 最后面的地址区域,也就
是将自己拷贝到 DRAM 最后面的内存区域中。
这么做的目的是给
Linux
腾出空间,防止
Linux kernel
覆盖掉
uboot
,将
DRAM
前面的区域完整的空出来。
在拷贝之前肯定要给
uboot
各部分
分配好内存位置和大小,比如
gd
应该存放到哪个位置,
malloc
内存池应该存放到哪个位置等
等。这些信息都保存在
gd
的成员变量中,因此要对
gd
的这些成员变量做初始化。最终形成一
个完整的内存“分配图”,在后面重定位
uboot
的时候就会用到这个内存“分配图”。
下面简单分析一下 board_init_f 函数。 board_init_f 函数定义在文件 common/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 }
第
1037~1054行代码无效,因为没有定义
CONFIG_SYS_GENERIC_GLOBAL_DATA。
第
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 函数
的
前半部分代码
如下:
(因为
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
......
62 NULL,
63 };
第
2
行,
setup_mon_len
函数设置
gd
的
mon_len
成员变量,此处为
__bss_end -_start
,也就
是整个代码的长度,即 uboot的镜像大小
。
第
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
定时
器一样。关于
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 函数
,通过串口输出一些信息:
U-Boot 2016.03 (Jul 07 2023 - 17:11:27 +0800)
第
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);
开启 UBOOT 的 DEBUG 功能的方法:
在 include/configs下有不用开发板的配置头文件,IMX6ULL使用的是 mx6ullevk.h,可以在mx6ullevk.h文件中加:
#define DEBUG
重新编译后的 uboot镜像文件,烧录到设备,就可以打印 uboot的debug信息。
Board: I.MX6U ALPHA|MINI
第
19
行,
print_cpuinfo
函数,用于打印
CPU 信息,打印结果如下:
CPU: Freescale i.MX6ULL rev1.1 792 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 35C
Reset cause: POR
第
20
行,
show_board_info
函数,用于打印板子信息,会调用
checkboard
函数,打印如下:
第
21
行,
INIT_FUNC_WATCHDOG_INIT
,初始化看门狗,对于
I.MX6ULL
来说是空函数
第
22
行,
INIT_FUNC_WATCHDOG_RESET
,复位看门狗,对于
I.MX6ULL
来说是空函数
第
23
行,
init_func_i2c
函数,用于初始化
I2C
,初始化完成以后会输出如下:、
I2C: ready
第
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
,空函数。
以上是
board_init_f 函数的前半部分。
具体是初始化一系列外设,比如串口、定时器,或者打印一些消息等。
下一篇文章简单分析 board_init_f函数的后半部分代码,具体是内存分配的部分。