从uboot启动到linux内核挂载rootfs

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值