1、链接脚本 u-boot.lds 详解
通过链接脚本可以找到程序的入口,如果没有编译过 uboot 的话链接 脚本为 arch/arm/cpu/u-boot.lds。 最终的链接脚本是在这个 链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds 文件。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ENTRY(_start) _start 在文件 arch/arm/lib/vectors.S 中有定义 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
“变量”值可以在
u-boot.map 文件中查找,
除了
__image_copy_start 以外,其他的变量值每次编译的时候可能会变化
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
从
u-boot.lds
中我们已经知道了入口点是
arch/arm/lib/vectors.S
文件中的
_start
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
_start 在 arch/arm/cpu/armv7/start.S 中实现 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
_start -> save_boot_params -> save_boot_params_ret 读取 cpsr 寄存器值,提取 bit0~bit4 数值。此五位数据设置处理器的工作模式 1、设置工作模式是 svc 2、关闭 FIQ 与 IRQ
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3、 设置向量表重定向 读取 CP15 SCTLR 寄存器值
CR_V
在
arch/arm/include/asm/system.h
中有如下所示定义
#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */
因此这一行的目的就是清除
SCTLR
寄存器中的
bit13
bit13
为
V
位,此位是向量表控制位
当为
0
的时候向量表基地址 为 0X00000000
为
1
的时候向量表基地址为
0XFFFF0000
软件不能 重定位向量表。
这里将 V
清零,目的就是为了接下来的向量表重定位
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
4、函数 cpu_init_cp15 用来设置 CP15 相关的内容,比如关闭 MMU Invalidate L1 I/D disable MMU stuff and caches | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
5、函数 cpu_init_crit 执行函数 lowlevel_init 在文件 arch/arm/cpu/armv7/lowlevel_init.S 中定义 设置 sp 指向 CONFIG_SYS_INIT_SP_ADDR,CONFIG_SYS_INIT_SP_ADDR 在 include/configs/mx6ullevk.h 文件中,在 mx6ullevk.h 中有如下所示定义。
IRAM_BASE_ADDR 和 IRAM_SIZE 在文件 arch/arm/include/asm/arch-mx6/imx-regs.h。
IMX6UL/IM6ULL 内 部 ocram
的首地址和大小
.config
中定义了
CONFIG_MX6UL
因此
IRAM_SIZE=0X20000=128KB
CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = 0x00900000
。
CONFIG_SYS_INIT_RAM_SIZE = 0x00020000 =128KB
。
GENERATED_GBL_DATA_SIZE
的值,在文件
include/generated/generic-asm-offsets.h
中有定义
此时
sp
指向
0X91FF00
,这属于
IMX6UL/IMX6ULL
的内部
ram
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
6、s_init 函数 s_init 函数定义在文件 arch/arm/cpu/armv7/mx6/soc.c 中
7、_main 函数定义在文件 arch/arm/lib/crt0.S 中 sp = CONFIG_SYS_INIT_SP_ADDR board_init_f_alloc_reserve 此函数有一个参数,参数为 r0 中的值,也 就是 0X0091FF00 此函数定义在文件 common/init/board_init.c 中 : 主要是留出早期的 malloc 内存区域和 gd 内存区域,返回值为新的sp指针地址:0x0091FA00 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
r9 寄存器存放着全局变量 gd 的地址: 0x0091FA00
uboot
中定义了一个指向
gd_t
的指针
gd
,
gd
存放在寄存器
r9
里面 的,因此 gd 是个全局变量, 在
include/asm-generic/global_data.h 里面有定义 也就是
gd
指向
0X0091FA00.
8、board_init_f_init_reserve
此函数在文件 common/init/board_init.c 中有定义
1) 初始化gd结构体(清零)
2) malloc_base 被赋值为
gd
基地址
+gd
大小=0X0091FA00+248=0X0091FAF8 再做16字节对齐,
gd->malloc_base=0X0091FB00
9、board_init_f
,此函数定义在文件
common/board_f.c
中
1) 初始化 gd
的所有成员变量,分配重定向之后 uboot 各部分的内存区域
2) 初始化一系列外设,比如串口、定时器,或者打印一些消息等
3) 主要在于
initcall_run_list() 包含一系列初始化函数
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp =
0X9EF44E90*/
1) 将sp指针更新,
GD_START_ADDR_SP=64
0X9EF44E90
是
DDR
中的地址,说明新的
sp
和
gd
将会存
放到
DDR
中,而不是内部的
RAM
了,sp 执行8字节对齐
2)
获取
gd->bd
的地址赋给
r9
,此时
r9
存放的是老的
gd
,这里通过获取
gd->bd
的
地址来计算出新的
gd
的位置。
GD_BD=0。
3) adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
设置 lr 为当前数值,r0 获取重定位偏移, lr = 当前值 + 重定位偏移
4) 读取重定位的地址,作为参数传进 relocate_code, 0X9FF47000。
10、relocate_code
此函数定义在文件
arch/arm/lib/relocate.S
11、relocate_vectors
此函数定义在文件
arch/arm/lib/relocate.S 对中断向量表做重定位
12、c_runtime_cpu_setup
,此函数定义在文件
arch/arm/cpu/armv7/start.S
初始化 Icache
13、清除 bss 段
14、board_init_r
(gd_t *id, ulong dest_addr) r0 gd地址,r1 重载目的地址
此函数定义在文件
common/board_r.c
中
调用
initcall_run_list
函数来执行初始化序列
init_sequence_r
init_sequence_r
也定义在文件
common/board_r.c
中
run_main_loop
行,主循环,处理命令。
uboot
启动以后会进入
3
秒倒计时,如果在
3
秒倒计时结束之前按下按下回车键,那么就
会进入
uboot
的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动
Linux
内
核 , 这 个 功 能 就 是 由
run_main_loop
函 数 来 完 成 的 。 r
un_main_loop
函 数 定 义 在 文 件
common/board_r.c
中
bootstage_mark_name
函数,打印出启动进度。
autoboot_command
函数,此函数就是检查倒计时是否结束?倒计时结束之前有
没有被打断?此函数定义在文件
common/autoboot.c
中
|
不管是
bootz
还是
bootm
命令,在启动
Linux
内核的时候都会用到一个重要的全局变量:
images
,
images
在文件
cmd/bootm.c
中有如下定义
bootm_headers_t images
|
do_bootz
函数
bootz
命令的执行函数为
do_bootz
,在文件
cmd/bootm.c
中有如下定义
调用
bootz_start
函数,
bootz_start
函数执行过程参考
32.3.3
小节。
调用函数
bootm_disable_interrupts
关闭中断。
do_bootm_states
调用函数
do_bootm_states
来执行不同的
BOOT
阶段
这里要执行的
BOOT
阶
段有:
BOOTM_STATE_OS_PREP
、
BOOTM_STATE_OS_FAKE_GO
和
BOOTM_STATE_OS_GO
。
|
bootz_start
函数
bootz_srart
函数也定义在文件
cmd/bootm.c
中
bootz_srart ->
do_bootm_states->bootz_setup->bootm_find_images
,调用函数
do_bootm_states
,执行
BOOTM_STATE_START
阶段
设置
images
的
ep
成员变量,也就是系统镜像的入口点,使用
bootz
命令启动
系统的时候就会设置系统在
DRAM
中的存储位置
调用
bootz_setup
函数,此函数会判断当前的系统镜像文件是否为
Linux
的镜
像文件,并且会打印出镜像相关信息
调用函数
bootm_find_images
查找
ramdisk
和设备树
(dtb)
文件
|
do_bootm_states
bootz
最 后 调 用 的 就 是 函 数
do_bootm_states
,而且 在
bootz_start
中 也 调 用 了
do_bootm_states
函数
BOOTM_STATE_OS_PREP
BOOTM_STATE_OS_FAKE_GO
BOOTM_STATE_OS_GO
BOOTM_STATE_START
!通过
函数
bootm_os_get_boot_func
来查找系统启动函数,参数
images->os.os
就是系统类型,根据这
个系统类型来选择对应的启动函数,在
do_bootz
中设置
images.os.os= IH_OS_LINUX
。函数返
回值就是找到的系统启动函数,这里找到的
Linux
系统启动函数为
do_bootm_linux
处理
BOOTM_STATE_OS_PREP
状态,调用函数
do_bootm_linux
,
do_bootm_linux
也是调用
boot_prep_linux
来完成具体的处理过程。
boot_prep_linux
主要用于处理环境变量
bootargs
,
bootargs
保存着传递给
Linux kernel
的参数
调用函数
boot_selected_os
启动
Linux
内核,此函数第
4
个参数为
Linux
系统镜像头,第 5
个参数就是
Linux
系统启动函数
do_bootm_linux
boot_selected_os
函数定义在文件 common/bootm_os.c 中
调用
boot_fn
函数,也就是
do_bootm_linux
函数来启动
Linux
内核
|
do_bootm_linux
函数
经过前面的分析,我们知道了
do_bootm_linux
就是最终启动
Linux
内核的函数,此函数定 义在文件 arch/arm/lib/bootm.c
如果参数
flag
等于
BOOTM_STATE_OS_GO
或者
BOOTM_STATE_OS_FAKE_GO
的话就执行
boot_jump_linux
函数。
boot_selected_os
函数在调用
do_bootm_linux
的时候会将
flag
设置为
BOOTM_STATE_OS_GO
执行函数
boot_jump_linux
,此函数定义在文件
arch/arm/lib/bootm.c
中
|
do_bootz()
{
boot_strat();
bootm_disable_interrupts();
do_bootm_states();
}
boot_strat()
{
do_bootm_states(BOOT_STATE_START);
images->ep = load_addr; // 获取linux镜像(zImage),保存在 images 成员变量 ep 中
bootm_find_images();
}
do_bootm_states(BOOT_STATE_START)
{
bootm_start();
}
bootm_find_images()
{
boot_get_fdt(); // 获取设备树,设备树首地址保存在 images 成员变量 ft_addr 中
}
do_bootm_states()
{
bootm_os_get_boot_func(); // 获取 linux 系统启动函数:do_bootm_linux()
boot_selected_os();
}
boot_selected_os()
{
boot_fn(); // 实际运行:do_bootm_linux
boot_prep_linux(); // 启动 linux 之前做一些其他处理,比如在设备树的 closen 节点下添加
// 子节点 bootargs, bootargs 子节点存放 bootargs 环境变量
boot_jump_linux();
}
boot_jump_linux()
{
announce_and_cleanup(); // 输出 "Strating kernel" 并且做一些清理工作
kernel_entry(); // 启动 linux 内核
}
变量 | 数值 | 描述 |
__image_copy_start
|
0x87800000
|
uboot
拷贝的首地址
|
__image_copy_end
|
0x8785dd54
|
uboot
拷贝的结束地址
|
__rel_dyn_start
|
0x8785dd54
|
.rel.dyn
段起始地址
|
__rel_dyn_end
|
0x878668f4
|
.rel.dyn
段结束地址
|
_image_binary_end
|
0x878668f4
|
镜像结束地址
|
__bss_start
|
0x8785dd54
|
.bss
段起始地址
|
__bss_end
| 0x878a8e74 | .bss 段结束地址 |