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 段结束地址 |