第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > uboot源码——内核启动分析

uboot源码——内核启动分析

时间:2022-12-03 11:13:52

相关推荐

uboot源码——内核启动分析

以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。

参考资料:/biaohc/p/6403863.html

总结:uboot启动linux内核的整个流程

开机时会出现倒计时,当没有按键按下的时候,uboot会读取出bootcmd这个环境变量,并使用rum_command函数来执行这个命令;

实质是执行了movi read kernel 30008000;bootm 30008000。movi read kernel,把sd卡中的kernel分区赋值到30008000内存处;bootm 30008000,真正的传参以及跳转到linux内核中执行(实际执行do_bootm()函数)。bootm(do_bootm()函数)首先要做的事情是判断这个内核镜像类型(zImage、uImage、设备树);通过对镜像文件的头文件的验证,以确定是哪种内核镜像,然后再把必须的信息储存起来(linux操作系统、ep的值等)。确定好以后调用do_bootm_linux函数来对内核传参并且启动内核。

一、uboot作用简介

uboot的主要作用是用来启动linux内核。

因为CPU不能直接从块设备中执行代码,需要把块设备中的程序复制到内存中,而复制之前还需要进行很多初始化工作,如时钟、串口、dram等;如要想让CPU启动linux内核,只能通过另外的程序,进行必要的初始化工作,在把linux内核中代码复制到内存中,并执行这块内存中的代码,即可启动linux内核;一般情况下,linux镜像储存在块设备中(SD卡、iNand、Nandflash等)。首先执行uboot代码,把块设备中的内核代码复制到内存地址0x30008000地址处,然后再执行bootm 0x30008000命令以启动内核代码。

二、基础知识

vmlinuz、Image、zImage、uImage的区别与联系

(1)linux内核代码经过编译链接,生成一个elf格式的可执行文件,即vmlinuz或者vmlinux;

此文件不能直接烧录。

(2)vmlinuz文件经过arm-linux-objcopy以后,生成一个Image镜像文件。

vmlinuz.elf文件大小为70M以上,而Image镜像文件为7M左右。

(3)Image文件在进一步经过压缩,并添加解压缩代码,形成zImage文件。

当zImage文件作为启动镜像来启动时,首先要解压这个文件,这个解压过程可以由uboot解压或者zImage文件本身自解压。zImage中除了linux内核的镜像以外,还有一些头文件以及这部分解压代码,所以内核实际上在addr地址中再加一个偏移量的位置。

(5)uImage是uboot自己专用的启动内核镜像,相对于zImage他们之间头文件有一定区别。

uImage现在基本上要属于过时的技术了,新一点的技术为设备树的启动方式;

三、UBOOT启动内核的代码分析

在启动UBOOT时,出现倒计时,如果没有按键按下,则会自动启动内核。

1、start_armboot()函数末尾的main_loop

下面这段代码在此main_loop函数中,作用是执行完倒计时函数之后,启动linux内核。

启动方式是s = getenv ("bootcmd");;假定不使用HUAH_PARSER,则run_command (s, 0);实际上就是读取环境变量bootcmd,然后执行这个命令。

2、bootcmd命令

bootcmd=movi read kernel 30008000; movi read rootfs 30B00000 300000; bootm 30008000 30B00000这两个命令完成linux内核启动。movi read kernel 30008000,把sd卡中kernel分区复制到30008000内存地址处;bootm 30008000,到内存地址处执行代码;执行bootm命令,实际执行do_bootm函数。

3、do_bootm()函数

(1)因为bootm 0x30008000,参数为2,因此有addr = simple_strtoul(argv[1], NULL, 16)

此时addr中的值为0x30008000。

(2)接着判断0x30008000偏移36字节以后的地址中的值

如果为0x016f2818,说明启动镜像为zImage,则输出boot with zImage。

(3)内核的操作系统类型,和内核真正的入口

hdr->ih_os = IH_OS_LINUX;zImage header中ih_os赋值为 IH_OS_LINUX;hdr->ih_ep = ntohl(addr);ih_ep中存放的是point address,这个值是真正内核代码的地址(起始执行入口地址);

(4)image变量

static bootm_headers_timages;images为uboot内定义的一个bootm_header_t格式的全局变量,用来完成启动过程的。bootm_header_t类型为一个结构体,如下图:包含一个image_header_t类型的指针,这个指针最后指向了0x30008000处的zImage header。包含一个image_header_t类型的结构体,后面会把0x30008000处的zImage header复制一份到此结构体;包含一个标志位 legacy_hdr_valid。如果上面两个赋值以后,把legacy_hdr_valid赋值为1;

(5)memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));

把hdr中的值复制一份到 image.legacy_hdr_os_copy中,即把内存地址0x30008000处设置好的zImage头复制一份到uboot的data段。

(6)直接跳转到after_header_check处,os为IH_OS_LINUX。

4、after_header_check

判断操作系统,然后调用do_bootm_linux函数

5、do_bootm_linux()函数

(1)获取环境变量bootargs;

(2)判断全局变量images中的legacy_hdr_valid是否为1,如果为1获取ep值,如果不为1则error。

(3)把ep强制类型换换为函数指针类型赋值给thekernel;

(4)从环境变量中读取machid的值,赋值给s,如果s不空则machid = 环境变量中machid的值,并打印machid;

----------------------------------uboot给内核传参-----------------------------------------------

6、uboot给内核传参

传参主要是uboot把与硬件有关的信息传给linux内核,如memory信息(多少bank、size、起始地址)、命令行信息、lcd 串口、initrd、MTD等信息。如要定义了任意一个CONFIG_XXXXX,则执行setup_start_tag(bd)

一、setup_start_tag函数

初始化第一块参数tag

(1)params = (struct tag *) bd->bi_boot_params;

gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);这句代码,把uboot中的全局变量(即gd)的bi_boot_params内存地址,强制转换为stuct tag* 类型,然后赋值给params;把PHYS_SDRAM_1+0x100这个地址设置为传参的起始地址;

(2)struct tag结构体

此结构体由一个stuct tag_header类型的结构体,加上一个由一系列结构体组成的union联合体组成;这一系列结构体中存放的就是与board有关的参数;

(3)hdr.tag 与hdr.size赋值

params->hdr.tag = ATAG_CORE;params->hdr.size = tag_size (tag_core); (4)对联合体中的结构体参数赋值

params->u.core.flags = 0;params->u.core.pagesize = 0;params->u.core.rootdev = 0; (5)对下一个params进行初始化

params = tag_next (params);//#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)),把params移动sizeof(tag_core)大小继续赋值

二、传递内存参数,setup_memory_tags函数

把内存每个bank的信息放到这里:第一个扇区的起始地址和大小,第二个扇区的起始地址和大小……

三、传递命令行参数,setup_commandline_tag函数

commandline是一个char *类型,指向环境变量中的bootargs的值,即:#define CONFIG_BOOTARGS "root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC0,115200"

四、setup_end_tag (bd):结束传参

-----------------------------------------uboot给内核传参结束-----------------------------------------------

7、uboot中最后一句代码

通过执行thekernel函数直接启动linux内核,传递三个参数:0、machid、传参的首地址;这三个参数是通过r0、r1、r2三个寄存器来传递,r0传递0、r1传递machid、r2传递传参的首地址。这样就把内核启动起来了。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。