bootm启动linux内核
boot/bootm.c
do_sql -> sql_export -> call_bootm -> do_bootm -> boot_run
boot_run:
被bootm_run,bootz_run,booti_run调用
初始化status然后调用bootm_run_states
bootm_run_states:
首次启动时:先调用bootm_start
再调用bootm_pre_load:获得镜像(image)起始地址,镜像预加载
调用bootm_find_os:确定操作系统
其他加载
bootm_measure:启动过程中执行关键组件测量的函数,安全启动之类的
加载操作系统bootm_load_os
内存重定位boot_ramdisk_high:这里把ramdisk重定位了
设备树重定位boot_relocate_fdt(但是设备树重定位函数没执行,反而是后面用的arm/lib里面的bootm文件中的函数)
获取操作系统引导函数:bootm_os_get_boot_func(这里os=5,代表linux)
调用不同状态的处理函数
boot_fn(BOOTM_STATE_OS_PREP, bmi) 这里实际上是调用do_bootm_linux(在第三页详述)
不出错就bootm_selected_os运行操作系统(ARMV7,运行之后不返回)
bootm_start:
获得image.verify yesno
调用boot_start_lmb初始化逻辑内存块(logic memory block),预留该范围
boot_stage_markname:在commom/bootstage.c中
image.status修改为start
return 0;
bootm_find_os:
调用boot_get_kernel:
genimg_get_format() :检查指针是否指向合法镜像
执行操作系统相应的初始化赋值
boot_get_kernel:
确定内核镜像的enable类型
输出第一句:printf("## Booting kernel from Legacy Image at %08lx ...\n",img_addr);
输出镜像相关信息
调用image_get_kernel验证legacy镜像完整性,返回指向镜像头部的指针
复制镜像头部
保存指向镜像头的指针
commom/bootstage.c
这里都是一些跟启动进度记录有关的,不是很重要
boot_stage_markname:
设置flag
调用bootstage_add_record
bootstage_add_record:
功能:增加阶段记录(“bootm_start”),首次时增加
调用函数打印启动进度
boot/image-board.c
boot_ramdisk_high:
boot_reolacate_fdt
这两个函数看起来厉害,但是实际上都没有调用他们。是加载具体操作系统,获取对应操作系统函数之后才进行的重定位。
env/common.c
env_get:
通过哈希表获取环境变量
arch/arm/lib/bootm.c
do_bootm_linux:
这个函数被调用了两次,第一次是提前准备,在bootm_run_states中直接调用。第二次调用不再返回,是真跳转到内核,在bootm_selected_os中调用
第一次这函数调用了boot_prep_linux,进行跳转前的准备工作
image_setup_linux
这个函数进行了设备树的重定位
之前一直在找bootargs在哪里设置,结果一直找不到,结果发现bootargs被添加到设备树的节点里面去了
第二次这函数调用boot_jump_linux,jump只被调用一次,直接跳转到kernel,并没有伪跳转,假跳转可能只在uboot开发调试阶段使用(假跳转在调试验证、步骤分析等过程中很有用)第一次时是假跳转跳

__________________________________________________________________________

bootargs 通常是在设备树的 /chosen 节点中设置的一个属性,用于传递内核命令行参数

unsigned long machid = gd->bd->bi_arch_number;//变量machid 保存机器ID,如果不使用设备树的话这个机器ID 会被传递给Linux内核,Linux 内核会在自己的机器ID 列表里面查找是否存在与uboot 传递进来的machid 匹配的项目,如果存在就说明Linux 内核支持这个机器,那么Linux 就会启动!如果使用设备树的话这个machid 就无效了,设备树存有一个“兼容性”这个属性,Linux 内核会比较“兼容性”属性的值(字符串)来查看是否支持这个机器。
char *s;
void (*kernel_entry)(int zero, int arch, uint params);//进入内核的函数指针,此函数有三个参数:zero,arch,params,第一个参数zero 同样为0;第二个参数为机器ID;第三个参数ATAGS 或者设备树(DTB)首地址,ATAGS 是传统的方法,用于传递一些命令行信息啥的,如果使用设备树的话就要传递设备树(DTB)。
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;//获取kernel_entry 函数,函数kernel_entry 并不是uboot 定义的,而是Linux 内核定义的,Linux 内核镜像文件的第一行代码就是函数kernel_entry,而images->ep 保存着Linux内核镜像的起始地址,起始地址保存的正是Linux 内核第一行代码,所以images->ep 就是函数kernel_entry 的地址。
U-boot启动(bootm_run_states之前)
要分析uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。
程序的链接是由链接脚本(链接脚本是编译生成的)来决定的,所以通过链接脚本可以找到程序的入口。lds后缀的文件就是连接脚本文件。
本项目根目录下的u-boot.lds就是要找的。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
入口是_start
相关文件:
vector.s 中断向量表 能找到_start
u-boot.map 地址映射表
u-boot:启动详细的代码调用流程
u-boot.lds:(arch/arm/cpu/u-boot.lds)
|-->_start:(arch/arm/lib/vectors.S)
|-->reset(arch/arm/cpu/armv7/start.S)
|-->save_boot_params(arch/arm/cpu/armv7/start.S)/*将引导参数保存到内存中*/
|-->save_boot_params_ret(arch/arm/cpu/armv7/start.S)
|-->cpu_init_cp15(arch/arm/cpu/armv7/start.S)/*初始化*/
|-->cpu_init_crit(arch/arm/cpu/armv7/start.S)
|-->lowlevel_init(arch/arm/cpu/armv7/lowlevel_init.S)
|-->_main(arch/arm/lib/crt0.S)
|-->board_init_f_alloc_reserve(common/init/board_init.c)/*为u-boot的gd结构体分配空间*/
|-->board_init_f_init_reserve(common/init/board_init.c) /*将gd结构体清零*/
|-->board_init_f(common/board_f.c)
DDR,即双倍速率同步动态随机存储器,是一种高速的、用于存储数据和程序的内存技术。
gd一般指global data
save_boot_params:保存引导参数到内存
save_boot_params_ret: 位置无关修复
dsb和isb,多处理器时的数据一致性
s_init 第一个C函数
_main
board_init_f_alloc_reserve :留出早期malloc区域和gd区域
board_init_f :
init_run_list:初始化序列函数、序列函数数组、串口IO配置、内核定时器等
这个函数用for循环调用一个函数队列,进行一系列初始化
relocate_code:用汇编实现的uboot重定位,执行此函数前r0赋值为GD_RELOCADDR。这里的重定位跟设备树、ramdisk一样,都是在低地址进行初始化完毕之后复制到高地址去
关键代码:
copy_loop:
ldmia r1!, {r10-r11} /* copy from source address [r1] */
stmia r0!, {r10-r11} /* copy to target address [r0] */
cmp r1, r2 /* until source end address [r2] */
blo copy_loop
console_init_r(common/console.c) :初始化控制台,也定义了一系列的初始化函数
|-->interrupt_init(arch/arm/lib/interrupts.c) : 初始化中断
|-->relocate_code(arch/arm/lib/relocate.S) /*主要完成镜像拷贝和重定位*/
&n

最低0.47元/天 解锁文章
807

被折叠的 条评论
为什么被折叠?



