U-Boot 启动流程详解

文章目录

  • 链接脚本 u-boot.lds 详解
  • Uboot启动流程

链接脚本 u-boot.lds 详解

要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过 uboot 的话链接
脚本为 arch/arm/cpu/u-boot.lds。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds文件
_start 后面就是中断向量表,从图中的“.section “.vectors”,
"ax”可以得到,此代码存放在.vectors 段里面。
使用如下命令在 uboot 中查找“__image_copy_start”:
grep -nR "__image_copy_start"
U-Boot 启动流程详解_第1张图片
打开 u-boot.map,找到所示位置
U-Boot 启动流程详解_第2张图片
u-boot.map 是 uboot 的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址, 932 行可以看到_image_copy_start 为 0X87800000,而.text 的起始地址也是0X87800000。
第 11 行是 vectors 段,vectors 段保存中断向量表,从图中我们知道了 vectors.S 的代码是存在 vectors 段中的。vectors 段的起始地址也是 0X87800000,说明整个 uboot 的起始地址就是 0X87800000,这也是为什么我们裸机例程的链接起始地址选择 0X87800000 了,目的就是为了和 uboot 一致。
第 12 行将 arch/arm/cpu/armv7/start.s 编译出来的代码放到中断向量表后面。
第 13 行为 text 段,其他的代码段就放到这里在 u-boot.lds 中有一些跟地址有关的“变量”需要我们注意一下,后面分析 u-boot 源码的时候会用到,这些变量要最终编译完成才能确定的!!!
U-Boot 启动流程详解_第3张图片在这里插入图片描述

变量”值可以在 u-boot.map 文件中查找,表 32.1.1 中除了__image_copy_start以外,其他的变量值每次编译的时候可能会变化,如果修改了 uboot 代码、修改了 uboot 配置、选用不同的优化等级等等都会影响到这些值。所以,一切以实际值为准!

Uboot启动流程

1、reset函数
bicne=bic + ne
①、reset函数目的是将处理器设置为SVC模式,并且关闭FIQ和IRQ.
②、设置中断向量。
③、初始化CP15
④、

2、lowlevel_init函数
CONFIG_SYS_INIT_SP_ADDR

#define CONFIG_SYS_INIT_SP_ADDR
(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)

#define CONFIG_SYS_INIT_SP_OFFSET
(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)

#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE
#define IRAM_SIZE 0x00020000

#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR
#define IRAM_BASE_ADDR 0x00900000 6ULL内部OCRAM

#define GENERATED_GBL_DATA_SIZE 256

0x00900000 + CONFIG_SYS_INIT_SP_OFFSE =>
0x00900000 + CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE =>
0x00900000 + 0x00020000 – 256 = 0x0091ff00

设置SP指针、R9寄存器

3、s_init函数
空函数。

4、_main函数

5、board_init_f函数
initcall_run_lis此函数会调用一系列的函数,这些函数保存在init_sequence_f数组里面,

version_string[] = U_BOOT_VERSION_STRING
#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - "
U_BOOT_TIME " " U_BOOT_TZ “)” CONFIG_IDENT_STRING

U_BOOT_VERSION = U-Boot 2016.03

TOTAL_MALLOC_LEN (CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)
CONFIG_SYS_MALLOC_LEN (16 * SZ_1M)
CONFIG_ENV_SIZE SZ_8K

mx6ullevk.c mx6ullevk.h这两个文件长打交道

6、relocate_code函数
relocate_code函数有一个参数,r0=gd->relocaddr=0X9FF47000,uboot重定位后的首地址。
r1=0X87800000 源地址起始地址。
r4=0X9FF47000-0X87800000=0X18747000 偏移。
r2=0x8785dc6c

当简单粗暴的将uboot从0X87800000拷贝到其他地方以后,关于函数调用、全局变量引用就会出问题。Uboot对于这个的处理方法就是采用位置无关码,这个就需要借助于.rel.dyn段。

8785dcf8 :
8785dcf8: 00000000 andeq r0, r0, r0

878042b4 :
878042b4: e59f300c ldr r3, [pc, #12] ; 878042c8
878042b8: e3a02064 mov r2, #100 ; 0x64
878042bc: e59f0008 ldr r0, [pc, #8] ; 878042cc
878042c0: e5832000 str r2, [r3]
878042c4: ea00d64c b 87839bfc
878042c8: 8785dcf8 ; instruction: 0x8785dcf8
878042cc: 87842aaf strhi r2, [r4, pc, lsr #21]

设置r3为878042b4+8+12=878042c8的值,r3=8785dcf8。这里并没有直接去读取rel_a的地址,而是借助了878042c。

878042c8叫做Label。

重定位以后
9ffa4cf8 :
9ffa4cf8: 00000000 andeq r0, r0, r0

9ff4b2b4:
9ff4b2b4: e59f300c ldr r3, [pc, #12] ; 878042c8
9ff4b2b8: e3a02064 mov r2, #100 ; 0x64
9ff4b2bc: e59f0008 ldr r0, [pc, #8] ; 878042cc
9ff4b2c0: e5832000 str r2, [r3]
9ff4b2c4: ea00d64c b 87839bfc
9ff4b2c8: 8785dcf8 ; instruction: 0x8785dcf8
9ff4b2cc: 87842aaf strhi r2, [r4, pc, lsr #21]

Label中的值还是原来的!必须要将8785dcf8换为重定位后的rel_a地址。读取9ff4b2c8里面的数据,也就是老的rel_a的地址=8785dcf8+0x18747000=0x9ffa4cf8

重定位以后,需要对所有的Label保存的数据加上偏移!!

8785dcec: 87800020 strhi r0, [r0, r0, lsr #32]
8785dcf0: 00000017 andeq r0, r0, r7, lsl r0
……
8785e2fc: 878042c8 strhi r4, [r0, r8, asr #5]
8785e300: 00000017 andeq r0, r0, r7, lsl r0

878042c8+offset = 读取新的Label处的数据+offset

完成这个功能在连接的时候需要加上”-pie”

7、relocate_vectors函数
设置VBAR寄存器为重定位后的中断向量表起始地址。

8、board_init_r函数
Board_init_r函数和board_init_f函数很类似。board_init_r也是执行init_sequence_r初始化序列。

9、run_main_loop函数
run_main_loop
	-> main_loop
		-> bootdelay_process 获取bootdelay的值,然后保存到stored_bootdelay

全局变量里面,获取bootcmd环境变量值,并且将其
返回
-> autoboot_command 参数是bootcmd的值。
-> abortboot 参数为boot delay,此函数会处理倒计时
-> abortboot_normal 参数为boot delay,此函数会处理倒计时
-> cli_loop uboot命令模式处理函数。
-> parse_file_outer
-> parse_stream_outer
-> parse_stream 解析输入的字符,得到命令
-> run_list 运行命令
-> run_list_real
-> run_pipe_real
-> cmd_process 处理命令,也就是执行命令

10、cli_loop函数

11、cmd_process函数
Uboot使用U_BOOT_CMD来定义一个命令。CONFIG_CMD_XXX来使能uboot中的某个命令。
U_BOOT_CMD最终是定义了一个cmd_tbl_t类型的变量,所有的命令最终都是存放在.u_boot_list段里面。cmd_tbl_t的cmd成员变量就是具体的命令执行函数,命令执行函数都是do_xxx。

cmd_process
	->find_cmd  从.u_boot_list段里面查找命令,当找到对应的命令以后以返回值的

形式给出,为cmd_tbl_t类型
->cmd_call
->cmdtp->cmd 直接引用cmd成员变量

三、bootz启动Linux内核过程
Uboot启动Linux内核使用bootz命令,bootm。。bootz是如何启动Linux内核?uboot的生命是怎么终止的呢?linux又是怎么启动的呢?

1、image全局变量

bootm_headers_t images;

2、do_bootz函数

tftp 80800000 zImage
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb

bootz 80800000 - 83000000

bootz命令的执行函数,do_xxxx,do_bootz是bootz的执行函数。

do_bootz
-> bootz_start
-> do_bootm_states 阶段为BOOTM_STATE_START
-> bootm_start 对images全局变量清零,
-> images->ep = 0X80800000
->bootz_setup 判断zImage是否正确
-> bootm_find_images
· -> boot_get_fdt 找到设备树,然后将设备树起始地址和长度,写入到images
的ft_addr和ft_len成员变量中。

-> bootm_disable_interrupts		关闭中断相关
-> images.os.os = IH_OS_LINUX;   表示要启动Linux系统
-> do_bootm_states  状态BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 、

BOOTM_STATE_OS_GO,
-> bootm_os_get_boot_func 查找Linux内核启动函数。找到Linux内核启动函数
do_bootm_linux,赋值给boot_fn。
-> boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); 就是do_bootm_linux。
-> boot_prep_linux 启动之前的一些工作,对于使用设备树来说,他会将
Bootargs传递给Linux内核,通过设备树完成。也就是向
Linux内核传参。
-> boot_selected_os BOOTM_STATE_OS_GO, do_bootm_linux
-> do_bootm_linux,BOOTM_STATE_OS_GO
-> boot_jump_linux
-> machid= gd->bd->bi_arch_number;
-> void (kernel_entry)(int zero, int arch, uint params);
-> kernel_entry = (void (
)(int, int, uint))images->ep; 0X80800000。
-> announce_and_cleanup 输出Starting kernel……
-> kernel_entry(0, machid, r2); 启动Linux内核。Uboot的最终使命,启动Linux内核。

zimage_header 的zi_magic为zimage的幻数,魔术数。应该为0x016f2818。前面有9个32位的数据,那么9*4=36,0~35,第36个字节的数据开始就是zimage的幻数。

3、do_bootm_states函数

4、bootm_os_get_boot_func函数

5、do_bootm_linux函数

你可能感兴趣的:(Linux,I.MX6U-ALPHA开发板,#,Ubuntu,单片机,linux)