当前位置: 代码迷 >> 综合 >> 【Linux】嵌入式Linux系统的移植(下篇:BootLoader,以U-Boot为例)
  详细解决方案

【Linux】嵌入式Linux系统的移植(下篇:BootLoader,以U-Boot为例)

热度:47   发布时间:2023-12-18 03:24:27.0

BootLoader

BootLoader的概念

BootLoader就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境,这就是所谓的引导加载程序(Boot Loader)。

在Flash存储中存放文件的分布图如下所示:

归根结底,BootLoader的任务是引导操作系统,所谓引导操作系统,就是启动内核,把内核加载到内存RAM中去运行。

本文中,BootLoader的起始地址为0x20000000,内核(uImage)的起始地址为0x20008000,根文件系统(initrd.img.gz)的起始地址为0x21000000

BootLoader的分类

通常,BootLoader是严重地依赖于硬件而实现的,特别是在嵌入式中。因此,在嵌入式里建立一个通用的BootLoader几乎是不可能的。因此,BootLoader的种类繁多,比如:

可以看出,不同的bootloader具有不同的使用范围。

其中最令人瞩目的就是有一个叫U-Boot的BootLoader,是一个比较通用的引导程序,而且同时支持X86、ARM和PowerPC等多种处理器架构。

U-Boot

U-Boot,全称 Universal Boot Loader,是遵循GPL条款的开放源码项目,是由德国DENX小组开发的用于多种嵌入式CPU的BootLoader程序,对于Linux的开发,德国的u-boot做出了巨大的贡献,而且是开源的。

U-Boot具有以下特点:

  1. 开放源码;
  2. 支持多种嵌入式操作系统内核,如Linux、NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS;
  3. 支持多个处理器系列,如PowerPC、ARM、x86、MIPS、XScale;
  4. 较高的可靠性和稳定性;
  5. 高度灵活的功能设置,适合U-Boot调试、操作系统不同引导要求、产品发布等;
  6. 丰富的设备驱动源码,如串口、以太网、SDRAM、FLASH、LCD、NVRAM、EEPROM、RTC、键盘等;
  7. 较为丰富的开发调试文档与强大的网络技术支持;

其实,把u-boot可以理解为是一个小型的操作系统。

U-Boot的常用命令

U-Boot的文件通过SD卡或者USB线可直接烧写到Flash中,然后就可以进行U-Boot的命令的执行了。

U-Boot的基本命令

  • print:打印U-Boot中的环境变量(打印格式:变量名=变量值)
  • setenv:设置U-Boot中的环境变量,重启后无效(设置格式:setenv 变量名 变量值;当变量值为空的时候,表示删除该环境变量)
  • saveenv:保存U-Boot中的环境变量,重启后有效
  • md:内存显示,显示特定内存地址的内容(设置格式:md 内存地址,如20008000)

U-Boot网络层相关命令

一般而言,将文件(例如:内核)从主板传输到目标板采用TFTP协议,这就涉及到U-Boot的网络层设置。而U-Boot的网络层的设置与环境变量ipaddr有关:

setenv ipaddr 192.168.10.3
saveenv

需要注意的是,该ip地址需要和虚拟机的网卡地址在同一网段,即需要Ping通(U-Boot为了精简,Ping协议并不完全,必须由目标板去Ping主机虚拟机)。这样才能完成目标板与主板虚拟机的网络层的通畅。

Ping通之后,就可以通过TFTP进行文件传输了。由于U-Boot内有TFTP的客户端,因此服务器端有两种可能:

  • 主板:下载TFTPD软件,设置目录并选择网卡
  • 主板虚拟机:下载服务器端软件,命令如下:
sudo apt-get install tftpd-hpa		//32位
sudo apt-get install tftpd openbsc-xinetd	//64位

然后TFTP的目录配置文件在/etc/inetd.conf中,比如:

sudo vi /etc/inetd.conf
...
tftp dgram udp wait noboby /usr/sbin/tcpd /usr/sbin/in.tftpd [目录]
...
sudo /etc/init.d/tftpd-hpa/restart		//重启服务

目标板进行TFTP需要知晓服务器端的ip地址、端口号、传输到的内存地址和需要传输的内容。服务器端的ip地址在环境变量serverip,端口号是TFTP规定好的(69号端口),剩下的两个参数就是tftp命令需要指定的:

tftp 20008000 hello.c

这段命令的意思为,从环境变量serverip的服务器端将hello.c文件通过TFTP协议传输到目标板内存地址为20008000的内存中。

U-Boot Flash相关命令

当调试完程序后,最终需要将其写入NandFlash中,否则程序仍在内存中,一旦重启就会消失。这就需要用到nand命令了。其一般格式为:

nand erase [NandFlash内部地址] [擦除大小]
nand [write/read] [内存地址] [NandFlash内部地址] [搬移大小]

通常,在使用write之前都需要先进行erase命令。例如:

nand erase 500000 1024			//擦除Flash5M地址1字节的内容
nand write 20008000 500000 1024			//将内存20008000以后1字节的内容写入Flash5M地址
nand read 20008000 500000 1024			//将Flash5M地址以后1字节的内容读取到内存20008000

这样就能保证,目标板重启之后数据也不会丢失了。

U-Boot启动内核命令

bootm命令用于启动一个操作系统映像。它会从内核映像文件(uImage)的头部取得一些信息,这些信息包括:映像文件的基于的cpu架构、其操作系统类型、映像的类型、压缩方式、映像文件在内存中的加载地址、映像文件运行的入口地址、映像文件名等。

紧接着bootm命令将映像加载到指定的地址,如果需要的话,还会解压映像并传递必要有参数给内核,最后跳到入口地址进入内核。

U-Boot启动内核,需要满足一定的条件:

  • 启动参数 bootargs
  • 根文件系统

启动参数bootargs,一般包括以下三项:

  1. root=:启动的根文件系统的位置相关信息
  2. init=:内核启动后的第一个可执行文件,即init进程
  3. console=:内核启动后,作为控制台的设备

根文件系统一般有两种方式:RamdiskNFS

Ramdisk:顾名思义内存磁盘,即将根文件系统放在内存中。本质上,RamDisk并非一个实际的文件系统,而是一种将实际的文件系统装入内存的机制,并且可以作为根文件系统。实际上它使用的文件系统是ext2。

此时的配置需要指定root的位置在RAM,同时还需要指定RAM中的具体位置大小,启动参数为:

setenv bootargs root=/dev/ram initrd=0x21000000,8M init=/linuxrc console=ttySAC0,115200
saveenv

确定完启动参数之后,就可以传输文件,并进行内核启动了,使用bootm命令:

tftp 20008000 uImage
tftp 21000000 initrd.img.gz
bootm 20008000

NFS:顾名思义网络文件系统。即允许网络中的计算机之间通过TCP/IP网络共享资源。在NFS的应用中,本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的文件,就像访问本地文件一样。这种方式,对于调试就非常方便

简单一点说,就是将根文件系统放在主板的虚拟机上,目标板通过NFS来进行访问。那么,显而易见,主板的虚拟机需要安装NFS的服务器端。

sudo apt-cache search nfs-				//记不得名称了可以用此命令搜索
sudo apt-get install nfs-kernel-server

安装完成后,需要进行NFS的配置,配置文件在/etc/exports中:

sudo vi /etc/exports
...
[目录] *(rw,sync,no_subtree_check)
...
sudo /etc/init.d/nfs-kernel-server restart		//重启服务

需要注意的是,根文件系统的后缀为.gz,是一个压缩包。当通过TFTP传输给目标板之后,目标板会自动对其进行解压操作,形成相应的文件夹。然而,如果采用NFS的方式,服务器端需要自身解压完,留给目标板进行访问:

gunzip initrd.img.gz			//解压为initrd.img,相当于一个ios文件
mount -t ext2 init.img [挂载目录] 				//挂载initrd.img

服务器端配置完成后,目标板的配置需要指定root的位置在NFS,同时还需要指定NFS中的具体ip端口目录绝对地址,端口号是NFS规定好的。

除此之外,U-Boot一旦启动内核之后,它就丧失了对目标板的主动权,此时它的环境变量ipaddr也丧失作用了。这个时候,目标板就是属于无ip的状态,自然NFS就无法连接上了。因此,还需要设置ip参数。启动参数为:

setenv bootargs root=/dev/nfs nfsroot=192.168.10.110:/home/rocky/work/rootfs ip=192.168.10.120 init=/linuxrc console=ttySAC0,115200
saveenv

确定完启动参数之后,就可以传输文件,并进行内核启动了,使用bootm命令:

tftp 20008000 uImage
bootm 20008000

采用NFS方式的好处就是,可以在主板虚拟机上写一个程序,并运用交叉编译器进行编译,编译完成后,自动就会同步到目标板上。这个时候,就可以直接在目标板上运行编译完的程序。即主板虚拟机编译,目标板运行

U-Boot自动启动内核命令

可以看出,上面的两种启动内核的方式都不能做到一上电就自动启动内核的作用。其实这都和U-Boot的环境变量有关:bootdelaybootcmd前者为启动后倒计时,后者为倒计时后自动执行的命令

就可以将内核、根文件系统都nand到Flash中,倒计时后从Flash中读取并启动内核:

setenv bootdelay 2
setenv bootcmd nand read 20008000 100000 200000;nand read 21000000 5600000 400000;bootm 20008000