【源码分析】Uboot启动流程详细分析(三)

笔者把Uboot启动流程分为了七个阶段:

第一阶段:CPU的关键初始化(中断、权限、MMU等)

第二阶段:初始化关键外设+打印信息+设置global_data信息

第三阶段:内存分配(用于uboot重定位)

第四阶段:重定位

第五阶段:继续初始化其他外设

第六阶段:开始主循环

第七阶段:bootz启动流程

6.6 第六阶段:开始主循环

uboot启动以后会进入3秒倒计时,如果在3秒倒计时结束之前按下回车键,那么就会进入uboot的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动Linux内核,这个功能就是由run_main_loop函数来完成的。

run_main_loop函数定义在common/board_r.c

run_main_loop函数的流程如下:

1、在一个死循环中调用main_loop

main_loop函数的流程如下:

1、调用 bootstage_mark_name 函数,用于打印出启动进度

2、执行函数 setenv,设置版本号环境变量 ver 的值为 version_string

3、调用cli_init函数,该函数跟命令初始化有关,用于初始化hush shell相关的变量

4、调用bootdelay_process函数,此函数会读取环境变量bootdelay和bootcmd的内容, 然后将bootdelay的值赋值给全局变量stored_bootdelay,返回值为环境变量bootcmd的值。

5、autoboot_command函数,用于判断倒计时结束前有没有被打断,无则调用默认bootcmd,有则返回调用函数

  • abortboot函数用于处理倒计时

  • 如果倒计时自然结束,就执行函数run_command_list,此函数会执行参数 s 指定的一系列命令,也就是环境变量 bootcmd 的命令,bootcmd 里面保存着默认的启动命令,因此 linux 内核启动

    • 这个就是 uboot 中倒计时结束以后自动启动 linux 内核的原理。
  • 如果倒计时结束之前按下了键盘上的按键,那么 run_command_list函数就不会执行,相当于 autoboot_command 是个空函数。就会回到main_loop函数执行 cli_loop 函数。该函数是命令处理函数,负责接收和处理输入的命令。

cli_loop 函数的流程如下:

cli_loop函数是uboot的命令行处理函数,负责接收和处理输入的命令

cli_loop->parse_file_outer->setup_file_in_str
						  ->parse_stream_outer->parse_stream
						  					  ->run_list->cmd_process

uboot 中命令是如何定义的

  • boot使用宏U_BOOT_CMD 来定义命令,宏U_BOOT_CMD定义在文件include/command.h中。
  • 定义这个宏的目的是为了定义一个 cmd_tbl_t 类型的变量,并初始化这个变量的各个成员。
  • uboot 中的每个命令都存储在.u_boot_list 段中,每个命令都有一个名为 do_xxx(xxx 为具体的命令名)的函数,这个 do_xxx 函数就是具体的命令处理函数。命令处理函数就是cmd_tbl_t的cmd成员。
  • uboot 中的命令表其实就是 cmd_tbl_t 结构体数组

cmd_process 函数的流程如下:

uboot 中的命令表其实就是 cmd_tbl_t 结构体数组

cmd_process->find_cmd->ll_entry_start 通过该函数得到得到数组第一个元素,也即uboot中命令表的起始地址
					 ->ll_entry_count 通过该函数得到得到数组长度,也即uboot中命令表的长度
					 ->find_cmd_tbl 通过该函数在命令表中找到所需的命令。找到则返回这个命令
           ->cmd_call->(cmdtp->cmd) cmd成员就是具体的命令处理函数,返回值为命令的执行结果

6.7 第七阶段:bootz启动流程

本节要解决三个问题

bootz是如何启动Linux内核?

uboot的生命是怎么终止的呢?

linux又是怎么启动的呢?

image全局变量
  • 不管是bootz还是bootm命令,在启动Linux内核的时候都会用到一个重要的全局变量: images,images在文件cmd/bootm.c定义

    bootm_headers_t images;
    
  • bootm_headers_t类型定义在include/image.h中

    typedef struct bootm_headers {
        ......
        /* os 成员变量是 image_info_t 类型的,为系统镜像信息 */
    	image_info_t os; 	/* OS 镜像信息 */
    	......
    	/* 下面这 11 个宏定义表示 BOOT 的不同阶段 */
        #define BOOTM_STATE_START (0x00000001)
        #define BOOTM_STATE_FINDOS (0x00000002)
        #define BOOTM_STATE_FINDOTHER (0x00000004)
        #define BOOTM_STATE_LOADOS (0x00000008)
        #define BOOTM_STATE_RAMDISK (0x00000010)
        #define BOOTM_STATE_FDT (0x00000020)
        #define BOOTM_STATE_OS_CMDLINE (0x00000040)
        #define BOOTM_STATE_OS_BD_T (0x00000080)
        #define BOOTM_STATE_OS_PREP (0x00000100)
        #define BOOTM_STATE_OS_FAKE_GO (0x00000200)/*'Almost' run the OS*/
        #define BOOTM_STATE_OS_GO (0x00000400)
        ......
    }
    
  • 类型image_info_t,也就是系统镜像信息结构体,定义在include/image.h中

do_bootz函数
  • bootz 命令的执行函数为 do_bootz,定义在文件 cmd/bootm.c 中

do_bootz函数的流程如下:

1、调用cmd/bootm.c中的函数bootz_start(cmdtp, flag, argc, argv, &images)

  • 调用函数do_bootm_states,执行BOOTM_STATE_START阶段:bootm_start函数
    • bootm_start函数会对images变量进行清零、设置images.state = BOOTM_STATE_START
  • 通过images的ep成员变量,设置系统镜像的入口点,images->ep = load_addr;
    • 使用 bootz 命令启动系统的时候就会设置系统在 DRAM 中的存储位置,这个存储位置就是系统镜像的入口点
  • 调用 bootz_setup 函数,用于获取zImage信息,判断zImage是否正确
  • 调用函数bootm_find_images查找设备树文件,找到以后就将设备树的起始地址和长度分别写到images 的 ft_addr 和 ft_len 成员变量中

2、调用函数 bootm_disable_interrupts,关闭中断

3、设置 images.os.os 为 IH_OS_LINUX,也就是设置系统镜像为 Linux,表示我们要启动的是 Linux 系统!后面会用到 images.os.os 来挑选具体的启动函数。

4、调用函数 do_bootm_states 来执行不同的 BOOT 阶段,这里要执行的 BOOT 阶段有:BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 和BOOTM_STATE_OS_GO

  • 调用bootm_os_get_boot_func,用于根据images->os.os找到系统对应的启动函数。此时就是boot_fn=do_bootm_linux

  • 接着,根据阶段的不同,会执行不同的逻辑

    • 对于BOOTM_STATE_OS_PREP阶段:调用boot_selected_os->boot_fn()->boot_prep_linux:该函数完成启动Linux之前的一些工作,对于使用设备树来说,他会将bootargs传递给Linux内核。也就是向Linux内核传参。

    • 对于BOOTM_STATE_OS_GO和BOOTM_STATE_OS_FAKE_GO阶段:调用boot_selected_os->boot_fn()->boot_jump_linux

      • kernel_entry(0, machid, r2);传入三个参数:第一个参数zero同样为0;第二个参数为机器ID;第三个参数ATAGS或者设备树(DTB)首地址。至此进入到内核函数,uboot执行完毕

        • Linux内核镜像文件的第一行代码就是函数kernel_entry,而images->ep保存着Linux 内核镜像的起始地址,起始地址保存的正是Linux内核第一行代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值