06步:使用u-boot加载内核

6.1 u-boot的存在的必要性

如果是之前玩过实体开发板的朋友,会疑惑一件事情,u-boot去哪了?
这是因为qemu自带bootloader功能,可以直接引导内核,有点类似于自带BIOS系统的电脑主板。
但是嵌入式设备通常是没有这样的条件的,所以我们如果真的要用QEMU完成仿真工作的话,还是得把u-boot加进来。

6.2 u-boot的编译和安装

下载完毕以后,存入目录/home/user/vexpress/u-boot里,老样子,解压,配置编译器,配置BSP,编译,运行

# cd /home/tftpboot
# mkdir uboot
# cd uboot
# wget https://ftp.denx.de/pub/u-boot/u-boot-2022.07-rc3.tar.bz2
# tar -xvf u-boot-2022.07-rc3.tar.bz2
# cd u-boot-2022.07-rc3
# gedit Makefile +272

和前面一样,输入以下内容,配置编译器

ARCH = arm 
CROSS_COMPILE = arm-linux-gnueabi-

配置和编译

# make vexpress_ca9x4_defconfig
# make -j 2

出现这个错误是因为没有openssl,装一个问题就解决了

# sudo apt install libssl-dev

这里我们先写一个测试脚本start_uboot.sh脚本

touch start_uboot.sh
chmod 777 start_uboot.sh
gedit start_uboot.sh

输入以下内容:

 qemu-system-arm    -M vexpress-a9 \
                     -kernel u-boot \
                     -nographic      \
                     -m 512M          \

运行u-boot:

# ./start_uboot.sh

效果如下图,qemu加载u-boot成功

6.3 u-boot+kernel+根文件系统全真模拟

细心的朋友可能发现了,4.2中我们加载u-boot的方法使用还是-kernel选项,也就是是把u-boot当做内核挂载的。
系统启动不可能同时挂载两个内核启动(只能选择一个启动)
有没有一个可行发方法来模拟呢?有!

u-boot实体机中我们常常采用tftp引导内核,我们也可以在启动u-boot以后,让u-boot通过tftp引导内核
我们需要做两件事情:1.搭建好tftp环境 2.构建网桥,让qemu可以访问,从而让qemu和ubuntu之间可以通过该网桥来访问

6.3.1 安装tftp和相关依赖,设置好路径

sudo apt-get install tftp-hpa tftpd-hpa xinetd uml-utilities bridge-utils

设置tftp配置路径文件:

# sudo vim /etc/default/tftpd-hpa

将下面的内容复制进来

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/tftpboot" #该路径即为tftp可以访问到的路径
TFTP_ADDRESS="0.0.0.0:69"
TFTP_OPTIONS="-l -c -s"

配置完以后,创建tftp目录,给最高权限,把内核、设备树、根文件系统、u-boot全部复制到这里,重启tftp服务

# cd  /home/tftpboot
# cp /home/tftpboot/uboot/u-boot-2022.07-rc3/u-boot .
# cp /home/tftpboot/kernel/linux-5.10.99/arch/arm/boot/uImage .
# sudo /etc/init.d/tftpd-hpa restart

6.3.2 修改网卡信息,设置桥接

# ifconfig                     #查看网卡名
# sudo gedit /etc/netplan/01-network-manager-all.yaml             #修改网卡名称设置

1.修改/etc/netplan/01/01-network-manager-all.yaml的信息配置,输入以下内容:

network:
  version: 2
  renderer: networkd
  ethernets:
      ens37:    #这里设置的是你还需要上网的网卡, ifconfig查看
          dhcp4: yes
      ens38:    #这里设置的是br0桥接到的网卡
          dhcp4: no
  bridges:        
      br0:        #这里设置的是br0网桥
          dhcp4: yes
          interfaces:
              - ens37 #声明br0网桥接入的网卡是ens37

输入sudo netplan apply使其生效,再输入ifconfig查看网卡信息

sudo netplan apply
ifconfig

2.修改/etc/qemu-ifdown信息配置

# sudo gedit /etc/qemu-ifdown

输入以下内容:

#! /bin/sh
# Script to shut down a network (tap) device for qemu.
# Initially this script is empty, but you can configure,
# for example, accounting info here.

echo sudo brctl delif br0 $1
sudo brctl delif br0 $1

echo sudo tunctl -d $1
sudo tunctl -d $1

echo brctl show
brctl show

3.修改/etc/qemu-ifup信息配置

#!/bin/sh

echo sudo tunctl -u $(id -un) -t $1
sudo tunctl -u $(id -un) -t $1

echo sudo ifconfig $1 0.0.0.0 promisc up
sudo ifconfig $1 0.0.0.0 promisc up

echo sudo brctl addif br0 $1
sudo brctl addif br0 $1

echo brctl show
brctl show

sudo ifconfig br0 192.168.33.145 # 这里设置的是网桥br0的地址

再启动修改start.sh再启动(start.sh存入tftpboot中)

# cd /home/user/tftpboot
# touch start.sh
# chmod 777 *
# ls -l
# gedit start.sh

输入启动指令

qemu-system-arm                 \
        -M vexpress-a9          \
        -kernel ./u-boot        \
        -nographic              \
        -m 512M                 \
        -nic tap,ifname=tap0    \
        -append "root=/dev/mmcblk0 rw console=ttyAMA0" \
        -sd  rootfs.ext3

root登录以后,./start.sh启动u-boot,此时敲击enter键,进入u-boot交互模式,输入以下内容:

setenv ipaddr   192.168.33.144                              # 设置u-boot这边的地址(和br0同一网段即可)
setenv serverip 192.168.33.145                            # 设置服务器地址(br0网桥的地址)
tftp 0x60003000 uImage                                       # 从tftp下载uImage
tftp 0x60500000 vexpress-v2p-ca9.dtb                     # 从tftp下载设备树
setenv bootargs "root=/dev/mmcblk0 rw console=ttyAMA0"       # 设置根文件系统挂载位置、权限、控制台设备
bootm 0x60003000 - 0x60500000                             # 设置启动地址

和前面kernel+busybox一样,挂载成功

6.4 u-boot的一些小修改

6.3中在u-boot中的配置还是很麻烦的,如果是实体机的话因为有在NAND/MMC Flash给了一个分区存参数,使用saveenv指令就能把参数存下来,但是我们这部分还没有把NAND/MMC相关支持完全做好,所以这样是不行的。

每次都要在u-boot这样处理,那也太麻烦了
有更简单的方法吗?
有!我们把u-boot地址、br0地址、内核加载命令bootcmd和根文件系统加载命令bootargs写入u-boot原程序里编译好了再放出来了就可以了。

6.4.1 设置u-boot和br0的IP地址

回到u-boot所在路径,再在vexpress的配置头文件里加入这么几行

# cd /home/tftpboot/uboot/u-boot-2022.07-rc3/
# gedit include/configs/vexpress_common.h +202

输入以下内容:

#define CONFIG_IPADDR   192.168.33.144
#define CONFIG_NETMASK  255.255.255.0
#define CONFIG_SERVERIP 192.168.33.145

输入以后,保存再次编译,再把新的u-boot复制到tftp路径

# cd /home/tftpboot/uboot/u-boot-2022.07-rc3
# make -j 2
# cp u-boot /home/tftpboot
# cd /home/tftpboot

6.4.2 设置启动选项

Boot options —> Enable a default value for bootcmd, 输入以下内容

tftp 0x60003000 uImage;tftp 0x60500000 vexpress-v2p-ca9.dtb;setenv bootargs 'root=/dev/mmcblk0 console=ttyAMA0';bootm 0x60003000 - 0x60500000;

6.4.3 编译, 传回tftpboot,再测试

# cd /home/tftpboot/uboot/u-boot-2022.07-rc3
# make -j 2
# cp u-boot /home/tftpboot
# cd /home/tftpboot
# su root
# ./start.sh

用户可以输入reboot重启测试一下,可以发现也是能正常工作的

不用买开发板,使用QEMU就可以学习的Linux内核开发实战视频教程:《Linux内核编程》,具有一线芯片原厂开发经验的驱动工程师录制,详情点击:王利涛老师个人淘宝店:Linux内核编程