uboot源码——内核启动分析

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

一、uboot作用简介

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

CPU不能直接从块设备中执行代码,因此需要把块设备中的程序复制到内存中,而复制之前还需要进行很多初始化工作,如时钟、串口、dram等。这些初始化工作,由uboot完成。初始化工作完成后,uboot把块设备中的内核代码复制到内存地址0x30008000地址处,然后再执行bootm 0x30008000命令以启动内核代码。

二、uboot启动内核的流程总结

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

实质执行“movi read kernel 30008000;bootm 30008000”。其中,“movi read kernel”表示把sd卡中的kernel分区赋值到30008000内存处;“bootm 30008000”表示真正的传参以及跳转到linux内核中执行,实际执行的是do_bootm()函数。

函数do_bootm()首先判断内核镜像类型(zImage、uImage、设备树),然后再把必需的信息(linux操作系统、ep的值等)储存起来,最后调用do_bootm_linux函数来对内核传参并且启动内核。

三、vmlinuz、Image、zImage、uImage文件

(1)vmlinuz、vmlinux文件

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

此文件不能直接烧录。

vmlinuz.elf文件大小为70M以上。

关于vmlinuz与vmlinux的区别,见博客:

Linux内核之vmlinux与vmlinuz - ldxsuanfa - 博客园

vmlinux vmlinuz zimage uimage 区别_suo_guang的博客-CSDN博客

vmlinux,vmlinuz,bzimage,zimage,initrd.img的区别与联系 - 走看看

(2)zImage文件

vmlinuz文件经过arm-linux-objcopy后,生成一个Image镜像文件,其大小为7M左右。

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

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

(3)uImage文件

uImage是uboot专用的启动内核镜像,基本上属于过时的技术。

新一点的技术是设备树的启动方式。

四、uboot启动内核的源码分析

1、分析start_armboot()函数末尾的main_loop()函数

 main_loop()函数中有一段代码如下。

  • 通过getenv()函数获取环境变量bootcmd的值,然后通过run_command()函数来执行。
  • 其中,bootcmd='movi read kernel 30008000;bootm 30008000'。
  • movi read kernel 30008000,表示把sd卡中kernel分区复制到30008000内存地址处。
  • bootm 30008000,表示到内存地址30008000处执行代码。
  • 执行bootm命令,实际执行do_bootm函数。

2、分析do_bootm()函数

do_bootm()函数位于uboot的/common/cmd_bootm.c文件中。片段代码如下。


(1)上述代码分析

  • 因为 “bootm 30008000”,所以argc==2,走的是else路线;argv[1]='30008000',这是字符串,要将字符串转化为ulong数字,simple_strtoul()完成此功能。
  • 接着判断0x30008000偏移36字节以后的地址中的值,如果为0x016f2818,说明启动镜像为zImage,则输出“boot with zImage”。
  • 因为没有开启虚拟地址映射功能,所以virt_to_phys(x)其实就是地址x。
  • 接着填充结构体变量hdr。主要填充两个成员,ih_os=IH_OS_LINUX,ih_ep=ntohl(addr)。前者表示内核的操作系统类型,后者表示内核代码的地址。ntohl()函数,将一个无符号长整形数从网络字节顺序转换为主机字节顺序,比如将0x12345678转换成0x78563412。
  • 然后把hdr复制一份到image.legacy_hdr_os_copy中,即把内存地址0x30008000处设置好的zImage头复制一份到uboot的data段。
  • 然后直接跳转到after_header_check处, 判断操作系统,调用do_bootm_linux函数。

(2)关于结构体变量hdr,其类型是image_header_t 

该结构体描述的是内核镜像的头部信息。

(3)关于结构体变量image,其类型是bootm_header_t 

3、分析do_bootm_linux()函数

do_bootm_linux()函数位于uboot的/lib_arm/bootm.c文件中。

(1)代码片段1与分析

  •  获取环境变量bootargs的值。
  • 判断全局变量images中的legacy_hdr_valid是否为1,为1则获取ep值。
  • 把ep强制类型换成函数指针类型,赋值给thekernel。
  • 获取环境变量machid的值,赋值给s;如果s不为空,则变量machid = 环境变量中machid的值;打印machid。

(2)代码片段2与分析:配置传参信息

  • 这段代码是uboot在配置将来传递给内核的信息。
  • uboot把与硬件有关的信息传给linux内核,比如memory信息(多少bank、size、起始地址)、命令行信息、lcd 串口、initrd、MTD等信息。
  • 如要定义了任意一个CONFIG_XXXXX,则执行setup_start_tag(bd)。

A、初始化第一块tag

1)代码流程分析

  • params = (struct tag *) bd->bi_boot_params; 这句代码把bd->bi_boot_params强制转换为stuct tag* 类型,然后赋值给params。bd是uboot中的全局变量gd的成员,gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100),这说明把“PHYS_SDRAM_1+0x100”这个地址设置为传参的起始地址。
  • 然后给hdr.tag 与hdr.size赋值,接着给联合体中的结构体参数赋值。
  • 从#define  tag_next(t)   ((struct tag *)((u32 *)(t) + (t)->hdr.size))可以看出,params指向下一个params,即把params移动sizeof(tag_core)大小继续赋值。
  • 总结:这块tag表示一个开始,除了说明传参的起始地址,没什么其他实际的信息。

2)上面的变量bd的类型是bd_t。

3)上面的变量params的类型是“static struct tag * ”。

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

结构体struct tag_header定义如下。

struct tag_header {u32 size;u32 tag;
};

B、传递内存参数:setup_memory_tags函数

  • 记录内存中每个bank的信息,包括每个bank的起始地址和大小。

 C、传递命令行参数:setup_commandline_tag函数

commandline是一个char *类型,指向环境变量中的bootargs的值,即:

#define CONFIG_BOOTARGS  "root=/dev/mtdblock4 rootfstype=yaffs2 init=/init console=ttySAC0,115200"

 D、初始化最后一块tag,即结束传参。

(3)代码片段3与分析:uboot的最后一句代码

这句代码通过执行thekernel函数直接启动linux内核,传递了三个参数:0、machid、传参的首地址。这三个参数是通过r0、r1、r2三个寄存器来传递的,其中r0传递0,r1传递machid,r2传递传参的首地址。

注意,theKernel()函数定义与赋值的过程如下:

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],bootm_headers_t *images)
{//省略部分代码void	(*theKernel)(int zero, int arch, uint params);//定义theKernel函数//省略部分代码if (images->legacy_hdr_valid) {ep = image_get_ep (&images->legacy_hdr_os_copy);}//省略部分代码theKernel = (void (*)(int, int, uint))ep;//将theKernel函数指针赋值为ep//省略部分代码theKernel (0, machid, bd->bi_boot_params);//执行theKernel函数}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460792.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

票据单号生产软件

有个老师要做excel表格,里面要罗列某票据还是什么单号的编号,格式如上图所示。一开始她说能不能写个excel函数,一拖就搞定~我觉得很难搞出来,就写个软件,生产单号保存在TXT文件,然后让她复制粘贴到excel表就…

uboot源码——C阶段的start_armboot函数

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 一、start_armboot函数简介 uboot第一阶段,start.S文件中进行一系列的SoC内部硬件的初始化,然后长跳转到start_armboot 函数中。 uboot第二阶段,start_armboot函数…

Hibernate的fetch

hibernate抓取策略fetch具体解释一、hibernate抓取策略(单端代理的批量抓取fetchselect(默认)/join)測试用例:Student student (Student)session.get(Student.class, 1);System.out.println(student.getName());System.out.print…

source insight的使用方法

1、下载与安装 下载与安装方法见链接。 “Insight3.exe”这个是汉化版,直接打开,不需要安装。 “Si35Setup.exe”这个是英文版,安装后需要自己找到应用图标并发送快捷方式到桌面。 2、快速新建工程 (1)点击 Project —…

TortoiseSVN检出链接(可用于与站点集成)

为什么80%的码农都做不了架构师?>>> TortoiseSVN(简称TSVN) 是一个 Windows 下的版本控制系统 Apache™ Subversion 的客户端工具。 如果你希望你的 Subversion 版本库对于别人可用,你可以在你的站点包含一个链接。 为…

使用抽象类还是接口(Java)

“要针对接口编程,不要针对实现编程。”那么,在java的世界里,这里的接口指的是什么呢?无非是以下两种实现方式: 方式1)抽象类(只能有一个超类) 方式2)接口Interface&…

.word的理解

我们在汇编文件中经常会见到.word指令,如下所示: label:.word express label,是一个标签,它是一个链接地址的别名,相当于一个指针。 express,表示链接地址处的内容,相当于指针所指向的内容。…

foreman架构的引入2-安装前环境准备

零基础学习Puppet自动化配置管理系列文档Foreman官网提供了每个版本非常完善的安装步骤,无论是源码安装还是rpm包安装都变得非常方便。而且Foreman通过puppet模块对安装步骤进行了封装并提供了大量的安装参数可以传输,相当的方便。不过由于其体系过大&am…

软件集成策略故事连载----对项目的不利影响竟然这么大

2.对项目的不利影响竟然这么大 项目经理老刘跟晓川说,等这一轮集成做完,一起聊一聊。晓川听了有点紧张。不过想一想,自己已经很努力了,也没有什么可担心的。其实关键是程序员提交的质量。倒正好可以借这个机会跟领导沟…

uboot源码——汇编阶段的start.S文件

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。 一、总结 1、关于阶段的定义 第一阶段,即在内部SRAM运行的阶段,简单地理解为汇编阶段。此阶段主要涉及start.S文件,在cpu/s5pc11x/目录下。第一阶段以ldr pc _sta…

TCP三次握手及四次挥手详细图解(转)

1.链接:http://www.cnblogs.com/hnrainll/archive/2011/10/14/2212415.html Andrew Huangbluedrum163.com 相对于SOCKET开发者,TCP创建过程和链接折除过程是由TCP/IP协议栈自动创建的.因此开发者并不需要控制这个过程.但是对于理解TCP底层运作机制,相当有帮助.而且对…

rm命令:linux如何反向删除文件

假如某目录下有多个文件:file1,file2……filen。 保留单个文件file1,其他文件删除的方法: # shopt -s extglob # rm -fr !(file1) 保留多个文件如file1和file2,其他文件删除的方法: # shopt -s extglob…

何以解养老之忧

何以解养老之忧 何以解养老之忧 很久没有写读后感了,记得那还是上高中以前做的事情。今天,2013年9月的某日。我读了一篇社论,题目为《如何让民众信任“以房养老”》的文章。 从文章当中得知,2012年底我国60周岁以上老年人口已达1.…

机器学习算法之旅

在理解了我们需要解决的机器学习问题之后,我们可以思考一下我们需要收集什么数据以及我们可以用什么算法。本文我们会过一遍最流行的机器学习算法,大致了解哪些方法可用,很有帮助。 机器学习领域有很多算法,然后每种算法又有很多延…

Android Handler的使用方法

如何让程序5秒钟更新一下Title.首先我们看一下习惯了Java编程的人,在不知道Handler的用法之前是怎么样写的程序,代码如下所示: package com.example.androidhandletest; import java.util.Timer;import java.util.TimerTask; import android.os.Bundle;import andro…

转:[C/C++]2014年7月华为校招机试真题(一)

[C/C]2014年7月华为校招机试真题(一)转载于:https://www.cnblogs.com/kira2will/p/4039521.html

df命令:显示系统上可使用的磁盘空间

以下内容源于网络资源的学习与整理,如有侵权请告知删除。 简介 df命令的英文全称即“Disk Free”,用于显示系统上可使用的磁盘空间。 日常普遍用该命令可以查看硬盘的挂载点,磁盘被占用了多少空间、还剩多少空间等信息。 默认单位为KB&#…

核心动画--基本动画

#pragma mark 动画暂停- (void)pauseAnimation{// 1. 取出当前的动画的时间点,就是要暂停的时间点CFTimeInterval pauseTime [self.myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];// 2. 设置动画的时间偏移量,指定时间偏移量的目的是让…

du命令:显示文件或者目录的大小

以下内容源于网路资源的学习与整理,如有侵权请告知删除。 由于磁盘空间将满,需要整理磁盘时,想知道哪些文件夹的内容过大,以及删除一些无用文件,du命令会非常有用。在用户目录下输入“du --max-depth1 -h”查看哪些文…

windows 下查看进程占用

2019独角兽企业重金招聘Python工程师标准>>> //查找出占用8086端口进程的ID netstat -nao | findstr8086 //本机输出效果为: TCP 0.0.0.0:8086 0.0.0.0:0 LISTENING 804 //很显然,进程ID是804 //找出ID为804的进程名 …