您好,欢迎来到百家汽车网。
搜索
您的当前位置:首页u_boot移植(九)之引导Linux 内核

u_boot移植(九)之引导Linux 内核

来源:百家汽车网
u_boot移植(九)之引导Linux 内核

通过前面几节的学习,我们已经将uboot成功的移植到了开发板。在这里我们思考一个问题 : \"我们移植uboot的目的是什么呢?\"

嗯,我们移植uboot的终极目的是用来引导Linux 内核的,接下来我们就来了解一下,uboot是如何引导Linux 内核?

一、uboot 和 Linux 内核之间的参数传递

我们知道,uboot启动后已经完成了基本的硬件初始化(如:内存、串口等),接下来它的主要任务就是加载Linux 内核到开发板的内存,然后跳转到Linux 内核所在的地址运行。

可能有人会问\"如何跳转呢?\内核所在的地址,这样CPU就会从Linux 内核所在的地址去取指令,从而执行内核代码。

需要注意的是,在执行Linux 内核代码前,uboot必须做好以下几件事情:

(1)CPU 寄存器的设置

<1>R0 = 0

<2>R1 = 机器类型ID

<3>R2= 传递给Linux 内核参数所在内存中的起始地址 (2)CPU 工作模式设置 <1>必须禁止中断(IRQs 和 FIQs)<2>CPU 必须为SVC模式(3)Cache 和 MMU 的设置<1>MMU 必须关闭

<2>指令Cache 可以打开也可以关闭 <3>数据Cache必须关闭

在ARM课程学习的过程中,我们讲解过函数间参数的APTCS标准,标准规

定函数的第[一、二、三、四]个参数分别用r0-r3来传递,这样大家应该就明白上面的第一步CPU寄存器的设置,实际上是给Linux 内核传递参数。 为什么要给Linux 内核传递参数呢?

因为Linux 内核启动的时候,需要一些参数。uboot启动的时候,完成了开发板上硬件的初始化,uboot可以自豪的说\"这块开发板我是支持的\是Linux 内核就很无语了,它是直接被加载内存中运行的,它对当前开发板的环境是一无所知的。所以,Linux 内核启动的时候就规定了,你uboot跳到我这里来运行的时候,必须告诉我一些信息,我要通过这些信息来判断是否支持当前开发板。

所以了,uboot就把机器ID写到CPU的R1中,Linux 内核运行的时候首先就从R1中读取机器ID来判断是否支持当前机器。

这个机器ID实际上就是开发板的ID,每个厂家生产出一款开发板的时候都会给它指定一个唯一的ID,大家可以到uboot源码的include/asm-arm/mach-types.h文件中去查看。

嗯,我们明白了为什么要传递机器ID,那R2的值又是什么意思呢?R2中存放的是块内存的基地址,这块内存中存放的是uboot 给Linux 内核的其他参数。这些参数有内存的起始地址、内存大小、Linux 内核启动后挂载文件系统的方式等信息。

很明显,参数有多个,不同的参数有不同的内容,为了让Linux 内核能精确的解析出这些参数,双方在传递参数的时候要求参数在存放的时候需要按照双方规定的格式存放。

在Linux 内核2.6以前,双方传递参数的格式用 struct param_struct结构

体描述,Linux 内核2.6以后双方传递的参数用tag 列表的形式描述。虽然目前的Linux 内核两种格式都支持,但是Linux 内核更喜欢用tag列表的方式传递参数,这里我们只介绍以tag列表的方式传递参数。嗯,还是先来看看以tag列表方式传递参数涉及到一些数据结构:

从上面可以看出struct tag 结构体由 结构体struct tag_header + 联合体u构成。结构体struct tag_header用来描述每个tag的头部信息,如tag的类型,tag 大小。 联合体u 用来描述每个传递给Linux 内核的参数信息。

下面以传递内存标记、命令行参数标记为例来说明参数的传递。(1)设置开始标记ATAG_CORE

涉及到结构体定义如下:

tag_size 和 tag_next 定义如下:

(2)设置内存标记

相关结构体定义如下:

(3)设置命令行参数标记

命令行参数就是一个字符串,一般用它来告诉内核挂载根文件系统的方式。由uboot的bootargs环境变量提供,它的内容有以下两种格式:<1>root=nfs nfsroot=192.168.1.100:/source/rootfs ip=192.168.1.200 init=/linuxrc console=ttySAC0,115200<2>root=/dev/mtdblock2 ip=192.168.1.200 init=/linuxrc console=ttySAC0,115200

告诉Linux 内核挂载根文件系统的方式,nfs 表示以NFS服务root的方式挂载根文件系统,/dev/mtdblock2 表示根文件系统在M

TD设置的第二个分区上。

nfsro告诉Linux 内核,以NFS方式挂载根文件系统时,根文件系统ot所在主机的IP地址和路径 ip 告诉Linux 内核,启动后它的ip地址 init告诉Linux 内核, 启动的第一个应用程序是根目录下的linuxrc

程序

conso告诉Linux 内核,控制台为ttySAC0,波特率为115200 le

相关结构体定义如下:

(4)设置结束标记ATAG_NONE

我们简单总结一下,上面设置的tag内容,总结如下:

嗯,我们明白了运行Linux 内核的时候,uboot需要给内核的传递的参数,接下来我们就来看看如何从uboot中跳到Linux 内核。

二、uboot 跳转到Linux 内核

在uboot中有两个命令可以用来跳转到指定的地址去执行,分别是go 和 bootm。 这两个命令的区别如下:

(1)go 命令仅仅修改pc的值到指定的地址,用法如下:

go 内存地址

(2)bootm命令uboot专门用来启动uImage格式的Linux 内核 ,它在修改pc的值到指定地址之前,会设置传递给Linux 内核的参数,用法如下:

bootm 内存地址

还是先来说说Linux 内核镜像常用的格式,当Linux 源代码编译好后,可以产生vmlinux、Image、zImage、bzImage、uImage等格式。

<1>vmlinux

vmlinuz是可引导的、可压缩的内核镜像,vm代表Virtual Memory.Linux支持虚拟内存,因此得名vm.它是由用户对内核源码编译得到,实质是elf格式的文件。也就是说,vmlinux是编译出来的最原始的内核文件,未压缩。<2>Image

Image是经过objcopy处理的只包含二进制数据的内核代码,它已经不是elf格式了,但这种格式的内核镜像还没有经过压缩.

<3>zImage

zImage是ARM linux常用的一种压缩镜像文件,它是由vmlinux加上解压代码经gzip压缩而成。

<3>bzImage

bz表示big zImage,其格式与zImage类似,但采用了不同的压缩算法,注意,bzImage的压缩率更高。

<4>uImage

uImage是uboot专用的镜像文件,它是在zImage之前加上一个长度为0x40的头信息(tag),在头信息内说明了该镜像文件的类型、加载 位置、生成时间、大小等信息.换句话说,若直接从uImage的0x40位置开始执行,则zImage和uImage没有任何区别。

通过上面的介绍我们知道,如果通过bootm来启动Linux 内核,Linux 内核必须为uImage格式。关于如何生成uImage格式Linux 内核镜像,我们在移植Linux 内核的时候在说,接下来我们来看看go命令和bootm命令的实现。

1. uboot 中bootm命令实现

bootm命令在uboot源码common/cmd_bootm.c文件中实现,关于它的实现比较复杂,大家只需要知道它的功能就可以了。它的功能如下:

(1) 读取uImage头部,把内核拷贝到合适的地方(2) 把参数给内核准备好,并告诉内核参数的首地址(3) 引导内核

在这里我们分析一下(2)、(3)的实现过程,bootm命令从uImage头中读取信息后,发现是 Linux 内核,就会调用do_boot_linux()函数,这个函数在

uboot源码的lib_arm/bootm.c文件中实现,内容如下:

2.uboot 中go命令的实现

go命令在uboot源码common/cmd_boot.c文件中实现,内容如下:

可以看到go命令在实现的时候,并没有设置参数,只是简单的跳转到指定的地址去运行。

大家注意看上面我框起来的地方,do_go_exec函数用__attribute__((weak))修饰,表示这个函数时一个弱符号,这个知识点我们在前面说过哦!为了让go命令也可以引导Linux 内核,我需要重写do_go_exec()函数。写什么东西呢?就是Linux 内核要求的那些东西呀! 我们在lib_arm/bootm.c文件中,添加如下代码:

很简单吧,do_bootm_linux()函数中已经把我们需要做的事情做好了,我们只需要调用它就可以了。

通过上面的分析我们知道,我们需要在uboot中设置bootargs这个环境变量值的,它最终作为命令行参数传递给Linux 内核的。接下来我们就来看看uboot中命令行参数的设置。

四、在uboot中添加自己的命令行参数

我们先来启动开发板,看看我们的uboot启动的时候,默认设置了哪些环境变量。

开发板启动信息如下:

输入print命令,输出如下:

可以看到,我们的uboot中默认设置的参数都是针对onenand的,不符合我们的要求。而且我们还会发现,我们的开发板启动的时候还输出了警告信息,告诉我们用CRC算法校验环境变量时候出错,最后选择了默认的环境变量。

我们先来看看,uboot中默认的环境变量是如何定义的。uboot默认的环境变量在common/env_common.c文件中定义,定义如下:

可以看到,默认的环境变量值都是通过相应的宏来设置的,接下来我们设置一下我们自己的环境变量,去掉uboot中默认的环境变量。修改include/configs/fsc100.h文件如下: (1)注释掉一下内容

(2)添加如下宏

修改完毕后,重新编译uboot,然后烧写到nand flash,烧写步骤如下: <1>将编译好的uboot.bin拷贝到tftp服务器的工作目录下

<2>启动开发板通过tftp 20008000 u-boot.bin命令,下载u-boot.bin到内存地址20008000

<3>通过nand erase 命令擦除Nand Flash 设备

<4>通过nand write 20008000 0 100000 命令,将内存地址20008000的uboot.bin写到Nand Flash的0地址 重新启动开发板,效果如下:

输入print命令,效果如下:

可以看到我们设置的默认环境变量,但是开发板启动的时候依然有警告说CRC校验错误,使用默认的环境变量。原因在于uboot启动的时候会先从Nand Flash中读取环境变量的内容,然后做一个CRC校验,如果校验错误就输出了警告信息。需要知道的是我们并没有在Nand Flash中保存环境变量,所以开发板启动的时候做CRC校验的时候就错误了。 怎么解决这个问题呢?

做法很简单,我们只需要输入save命令将当前默认的环境变量保存到Nand Flash即可。做法如下:

可以看到uboot默认将环境变量保存在Nand Flash的0x40000起始地址。需要注意的是,你的uboot大小一定不能超过0x40000

即256Byte,否则在save保存环境变量的时候,就会把uboot的内容破坏掉,最后出现的效果就是你在uboot中输入任何命令都无法识别。如果想修改环境变量的值怎么办呢?

通过命令\"setenv 环境变量名 内容\"进行设置就可以了,设置成功之后用save命令保存一下就可以了。

貌似到目前为止,我们都没有用uboot启动内核,接下来我们就用我们的uboot来启动内核。

五、uboot 引导 Linux 内核的两种方式

1. uboot 通过网络引导Linux 内核

从上图可以看到,uboot 运行起来后,通过网络服务将Linux 内核从PC机上下载到开发板的内存中,然后在跳到内核所在的内存地址去执行。 做法如下:

<1>通过 tftp 20008000 zImage 命令下载内核镜像zImage到内存地址20008000

<2>通过go 20008000命令跳到20008000地址运行Linux 内核。 需要注意的是,我们的go命令已经做过修改了哦,如果没有做过修改是不能正常引导Linux 内核的,原因你懂得。效果如下:

通过网络的方式引导Linux 内核,多用于开发调试阶段,这样我们只需要将编译好的Linux 内核拷贝到PC主机的TFTP服务的工作目录中,然后在开发板那边用tftp命令下载到开发板的内存中运行就可以测试 Linux 内核是否OK。

2.uboot 从Nand Flash 中引导Linux 内核

从上面我们可以看到,我们已经将嵌入式系统完全烧写到 Nand Flash 中,当开发板一上电启动的时候,uboot 会先运行起来,接着uboot 会用nand read 命令从Nand Flash中将Linux 内核读到内存的某个地址,然后跳到Linux 内核所在的地址去运行。

我们先将Linux 内核烧写到Nand Flash ,做法如下: <1>tftp 20008000 zImage

<2>nand erase 20000000 300000

<3>nand write 20008000 20000000 300000效果如下:

接下来我们从Nand Flash引导Linux 内核,做法如下: <1>nand read 20008000 200000 300000<2>go 20008000效果如下:

uboot 从Nand Flash 中引导Linux 内核这种方式, 用于最终在产品发布的时候。

以上操作需要注意的一点是,如果操作的是uImage,则需要用bootm命

令引导Linux 内核。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- baijiahaobaidu.com 版权所有 湘ICP备2023023988号-9

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务