LV020-网络挂载系统
一、概述
1. 目的?
前面我们已经可以将正点原子出厂的系统烧写到 SD 卡或者 EMMC 中了,但是在开发过程中我们是要不断修改镜像和设备树以及根文件系统的,这样调试的话每次都要重新烧写,就格外的麻烦,其实 uboot 是支持从 tftp 下载设备树和 linux 镜像的,然后支持从 nfs 挂载根文件系统,这一节我们就来试一试吧。
由于还未开始学习 uboot 和 linux 内核、根文件系统等的移植,所以先使用正点原子出厂系统,先熟悉操作流程。
2. 网络环境搭建
二、IP 地址配置
1. Ubuntu
ubuntu 的 IP 地址如下:
sumu@sumu-vm:~$ ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.15.128 netmask 255.255.255.0 broadcast 192.168.15.255
#......
ens37: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.10.101 netmask 255.255.255.0 broadcast 192.168.10.255
#......2. windows
windows 以太网口的信息如下

IPv4地址 192.168.10.100
IPv4子网掩码 255.255.255.0
IPv4默认网关 192.168.10.13. Linux 开发板
这个其实也不需要,主要是 uboot 阶段会设置 IP,但是这里记录一下吧:
eth0 Link encap:Ethernet HWaddr 88:5f:47:ce:a8:7c
inet addr:192.168.10.103 Bcast:192.168.10.255 Mask:255.255.255.0
#......三、文件准备
1. 需要哪些文件?
前面学习烧写系统的时候,我们会向开发板中烧写 uboot、linux 内核、还有设备树这几个主要的文件。uboot 是我们要使用的,只能提前通过脚本或者 mfgtool 烧写到开发板中,这里主要是从 tftp 加载 linx 内核和设备树,从 nfs 挂载根文件系统。所以我们需要的是 linux 内核镜像 zImage,根文件系统 rootfs.tar.bz2 以及设备树文件。根文件系统 rootfs 和 linx 内核文件很容易就找到了,但是用的是哪个设备树?(其实我们在设置开发板 uboot 参数的时候会指定设备树的名称,但是这里使用的是正点原子出厂系统做测试,就和出厂系统使用的保持一致吧)

这个时候,要是我们自己在移植系统的话很好选择,可是现在只是在出厂系统验证行网络启动的功能,我们其实可以从 uboot 中获取使用的设备树的名字,我们开机进入 uboot 中:

在这读秒变成 0 前按下 Enter 就会进入 uboot,我们敲下 print 命令可以看到使用的设备树是 imx6ull-14x14-emmc-4.3-800x480-c.dtb 这个:

2. 获取文件
上面知道我们需要准备 zImage、dtb 和根文件系统,我们从正点原子出厂开发工具中拷贝出来:
也可以从这里下载(我保存了一份在百度云)
通过网盘分享的文件:alpha-mfgtool 提取码: h5eg

如下所示:

将对应的文件拷贝到虚拟机中的 ubuntu 中,分别放在 ubuntu 的指定目录下。
- 设备树和zImage放在
~/3tftp/imx6ull-alpha目录中:

- 根文件系统放在
~/4nfs/imx6ull-alpha目录,并且需要解压,我们执行:
tar vxjf rootfs.tar.bz2
四、uboot 下的操作
1. 进入 uboot
要是使用自己移植的 uboot 的话,开机时会有如下提示:

在读秒结束前,按下任意按键,这里就会停在 uboot 界面啦。使用的是正点原子出厂系统的话,会是这样的,操作是一样的:

2. 网络环境变量设置
由于我们使用的是正点原子出厂的 uboot,所以网络的驱动是移植好的,我们可以直接进行相关网络参数的配置。为防止可能之前修改过环境变量而导致无法挂载,我们可以使用 env default -a 指令先清除环境变量。
=>env default -a;saveenv网络参数配置如下
=>setenv ipaddr 192.168.10.103 # 开发板 IP 地址
=>setenv ethaddr 00:04:9f:04:d2:35 # 开发板网卡 MAC 地址 b8:ae:1d:01:00:00
=>setenv gatewayip 192.168.10.1 # 开发板默认网关
=>setenv netmask 255.255.255.0 # 开发板子网掩码
=>setenv serverip 192.168.10.101 # 服务器地址,也就是 Ubuntu 地址
=>saveenv # 保存环境变量
=>print ipaddr ethaddr gatewayip netmask serverip配置完我们可以打印看一下这些环境变量的值对不对:

然后我们 ping 一下 ubuntu 和 windows:
=>ping 192.168.10.100 # windows
=>ping 192.168.10.108 # ubuntu20.04
这样表示可以正常通信,这里可能会有几个个问题:
(1)那就是 ubuntu 若是使用的双网卡的话,它可能会 ping 失败,由于我之前是没有找到怎么放开发板默认与 ubuntu 的桥接网卡通信,所以这里我就直接把 NAT 网卡关掉了,但是吧,我后来使用的 ubuntu20.04 似乎并没有这个问题,上图中是 ping 通了的。
(2)和 windows 进行 ping 的时候也有时候 ping 不通,后来发现可能是因为我使用的是扩展坞扩展的网口的原因,所以这里就可以重启一下网口,可以使用 win+r 组合键调出运行框,然后输入以下命令:
ncpa.cpl然后就会打开控制界面,我们可以双击扩展的以太网口,进行禁用,然后重新启动即可。
(3)uboot 阶段, 虚拟机 ping 开发板可能是没有反馈的,也可以不用虚拟机去 ping 开发板,但是我这里看起来是 ping 通了。
(4)这个 MAC 地址我是按文档上写的,其实好像随便写一个也行,比如我自己移植系统的时候测试用的 b8:ae:1d:01:00:00。
(5)首次设置完开发板的网络参数的时候可能会出现下面的问题,我以为是出厂系统有问题,但其实重启一下就可以了:

3. 修改 bootcmd 和 bootargs
3.1 bootcmd
我们在 uboot 输入以下命令:
=> setenv bootcmd 'tftp 80800000 /imx6ull-alpha/zImage\;tftp 83000000 /imx6ull-alpha/imx6ull-14x14-emmc-4.3-800x480-c.dtb\;bootz 80800000 - 83000000'
=> saveenv这里主要是设置从哪里下载设备树和 linux 镜像,我们在 ubuntu 中,tftp 服务器目录下相关文件如下:

这里在进行配置的时候 /home/sumu/3tftp 是作为根目录的,所以对于开发板来说,zImage 所在的目录为 /imx6ull-alpha/zImage。
注意:80800000 - 83000000 中的 - 两边各有一个空格。
3.2 bootargs
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.101:/home/sumu/4nfs/imx6ull-alpha,proto=tcp rw ip=192.168.10.103:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc'
=> saveenv
# 也可以换行写, 注意用\换行时前面要有一个空格
# 注意换行符后面直接接回车键,不要有空格,到下一行再去排版。
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs \
nfsroot=192.168.10.101:/home/sumu/4nfs/imx6ull-alpha,proto=tcp rw \
ip=192.168.10.103:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off \
init=/linuxrc'
=> saveenv格式说明:
=> setenv bootargs 'console=开发板串口,波特率 root=挂载方式 nfsroot=虚拟机ubuntu的IP地址:虚拟机ubuntu中文件系统路径,proto=传输协议 读写权限 ip=开发板IP地址:虚拟机ubuntu的IP地址:网关地址:子网掩码::开发板网口:off init=/linuxrc'
=> saveenv这里主要是设置 nfs 根文件系统的挂载目录,我们在 ubuntu 中,nfs 目录下相关文件如下:

这里配置的时候就直接配置完整的绝对路径即可。
【注意】这个地方有个坑,当时用的自己移植的系统,所以加上了 init=/linuxrc 这一句,要是使用正点原子官方出厂系统测试的话,这里不能加这个,不然会有问题。
4. 重启 uboot 测试
接下来就是重启测试了,我们可直接按下复位键或者在 uboot 中输入 re ,再按下回车即可。
4.1 uboot 启动
最开始就是 uboot 的启动,我们可以看到会有以下打印信息,这个图是之前自己移植的 uboot 的启动打印:

这里读秒结束后,便会开始运行 bootcmd 中的命令了。正点原子出厂系统的话,打印是这样的:

4.2 下载设备树和 linux 镜像

到这一阶段,会先检测网络是否正常,若是正常,则会打印出服务器的地址和开发板的地址。然后就会开始下载 linux 镜像文件 zImage。接着就会开始下载设备树,同样会先检测网络,然后打印服务器信息,最后开始下载文件。
4.3 启动 linux 镜像
接下来就是启动 linux 内核了:

后边还有很多,这里就截取了一部分,其实要是用的 ubuntu16.04 及以下的话,这个时候系统应该是可以正常启动的,但是我用的版本较高,所以最后挂载根文件系统失败了:

自己移植的系统 NFS 挂载失败的现象
最后挂载失败是这样:
这里会发现的 VFS 就表示从 nfs 挂载根文件系统失败了。我们后边再说怎么处理,其实到这里按理说正常是已经实现了从 tftp 下载 linux 镜像和设备树,从 nfs 挂载镜像,这里怎么处理可以看《五、问题处理——1.ubuntu20.04 NFS 版本问题》的笔记。 不出意外的话又出意外了,解决了 NFS 挂载不上的问题后,又会有如下报错:

这个好像是根文件系统的问题,是什么原因呢?原来是自己埋的坑啊,是因为加了一句 init=/linuxrc,去掉就可以了。
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs \
nfsroot=192.168.10.108:/home/sumu/4nfs/imx6ull-alpha,proto=tcp rw \
ip=192.168.10.109:192.168.10.108:192.168.10.1:255.255.255.0::eth0:off'
=> saveenv修改之后就能挂载成功了,也能正常启动:

5. 恢复原有启动方式
我们改了 uboot 的环境变量,怎么恢复默认?可以执行以下命令:
=>env default -a;saveenv这样就会回复默认的参数了。
五、问题处理
1. ubuntu20.04 NFS 版本问题
1.1 确认当前支持的 nfs 版本
sudo cat /proc/fs/nfsd/versions
(1)如果版本信息中包括了+2,而 uboot 的 nfs 服务仍不可使用,则这个方式应该也还是无法解决问题,就需要查找其它资料了。
(2)如果版本信息中包括了-2,则 ubuntu 内核版本不需要修改,直接跳到 修改 nfs 配置文件就可以了。
(3)如果版本信息中,最低是+3,则需要先切换 ubuntu 的 Linux 内核。这是因为 Linux 的 6.2 以上版本内核,将不再支持 nfs2。
1.2 切换 Linux 内核
1.2.1 查看当前存在的内核版本
使用命令:
uname -r如果显示的版本是 6.2 以上,则必须先降低 Linux 版本,比如这里是 6.5 版本:
6.5.0-15-generic使用命令查看当前存在的内核镜像:
dpkg --get-selections | grep linux-image
如果没有 6.2 以下版本,(后面要写着 install)则需要安装内核镜像才行。就算是写着 install,也建议按照下面的步骤再安装一次,因为可能 image 装了,但是 headers 之类的没装!
1.2.2 安装指定版本的 Linux 内核
- (1)安装镜像
sudo apt-get install linux-image-5.19.0-50-generic- (2)安装内核头文件
sudo apt-get install linux-headers-5.19.0-50-generic- (3)安装内核模块
sudo apt-get install linux-modules-5.19.0-50-generic- (4)安装驱动
sudo apt-get install linux-modules-extra-5.4.0-99-generic- (5)要是出现问题的话,修复命令如下:
sudo apt --fix-broken install- (6)检查是否安装成功
dpkg -l | grep 5.19.0-50-generic如下为成功

1.2.3 修改启动配置,使用新的 Linux 内核启动
修改启动配置,打开文件:
sudo vim /etc/default/grub找到 GRUB_DEFAULT = 0,修改为:
GRUB_DEFAULT=“Advanced options for Ubuntu>Ubuntu,with Linux 5.19.0-50-generic”找到 GRUB_TIMEOUT = 0,修改为:
GRUB_TIMEOUT=20找到 GRUB_CMDLINE_LINUX_DEFAULT =“quiet splash”,修改为
GRUB_CMDLINE_LINUX_DEFAULT=“text”保存退出文件。然后更新 grub:
sudo update-grub最后重启系统:
sudo reboot1.2.4 开机选择合适的 Linux 内核版本
开机后会进入下面的页面,选择下面高亮这个进入:

选择合适的 Linux 内核进行启动:

启动完成后,再次使用命令查看 linux 内核信息:
uname -r会发现版本切换成功:

保证 Linux 版本是 5.x 的就可以正常开启使用 nfs2 服务。
1.3 NFS 配置
前面已经安装过 nfs 了,接下来我么直接开始配。
1.3.1 修改/etc/default/nfs-kernel-server 文件
打开配置文件:
sudo vim /etc/default/nfs-kernel-server修改下面几个地方,要是没有就直接添加就好了:

不出意外的话,后面肯定出意外了,重启之后查询启动情况会报这些问题:
(1)--ver 相关报错:

(2)--udp 相关报错:

根据报错提示,修改如下:

1.3.2 修改/etc/hosts 文件
打开 hosts 文件:
sudo vim /etc/hosts添加开发板的 ip 和 nfs 服务器的地址:

ip 是开发板的 ip,后面那个是 nfs 文件系统存放的绝对路径。
1.3.3 修改/etc/nfs.conf 文件
打开文件:
sudo vim /etc/nfs.conf这个文件在 ubuntu20.04 中好像没有,那就直接新建好了:

1.4 重启 nfs 服务
sudo exportfs -ar
sudo service nfs-kernel-server restart执行下面的命令确认 nfs 服务开启:
netstat -a | grep "nfs"
1.5 查看 nfs 版本号
sudo cat /proc/fs/nfsd/versions
可以看到 nfs 版本中已经有了+2。然后我们重启开发板应该就可以正常挂载根文件系统了。
参考资料:
2. ubuntu22.04 NFS 版本问题
说明:这个问题是最开始的学习的时候用 ubuntu22.04 出现的,以及解决办法,后来用了 20.04,看上一节笔记就是了,这里可以作为参考。
对于上边开发板无法从新版本的 ubuntu 挂载 nfs 上的根文件系统的问题,这里我们来看一下怎么处理。(这个问题比较顽固,而且由于后面 ubuntu22.04 下总是有各种问题,所以后来我就换回 ubuntu16.04 啦,这里拿来参考下好了)。
在网上查阅资料会发现,linux 内核支持的 nfs 协议是 2.0 的,但是新版本的 ubuntu 默认是不支持 nfs2.0 的,我们可以看一下当前系统支持的 nfs 协议版本:
sudo cat /proc/fs/nfsd/versions
可以看到,新版本的 ubuntu 是没有支持 nfs2.0 协议的,所以开发板和 ubuntu 通过 nfs 通信失败也是正常,那怎么处理?网上一搜就有很多资料,一般都是修改下边这个文件,添加 nfs2.0 协议:
sudo vim /etc/default/nfs-kernel-server这个文件修改后的完成内容为:
# Number of servers to start up
RPCNFSDCOUNT="8"
# Runtime priority of server (see nice(1))
RPCNFSDPRIORITY=0
# Options for rpc.mountd.
# If you have a port-based firewall, you might want to set up
# a fixed port here using the --port option. For more information,
# see rpc.mountd(8) or http://wiki.debian.org/SecuringNFS
# To disable NFSv4 on the server, specify '--no-nfs-version 4' here
RPCMOUNTDOPTS="--manage-gids"
# Do you want to start the svcgssd daemon? It is only required for Kerberos
# exports. Valid alternatives are "yes" and "no"; the default is "no".
NEED_SVCGSSD=""
# Options for rpc.svcgssd.
RPCSVCGSSDOPTS=""
RPCNFSDOPTS="--nfs-version 2,3,4 --debug --syslog"其实就是在最后一行添加了一个 2,修改完后我们保存,然后重启 nfs 服务:
sudo service nfs-kernel-server restart然后再查看一下 nfs 的支持版本:
sudo cat /proc/fs/nfsd/versions
发现并未生效,原因在哪呢?原因在于我们的系统实在太信了,所以还有其他的文件需要配置,哪个文件?我们可以去 ubuntu 官网搜索一下(Enterprise Open Source and Linux | Ubuntu)会发现这样一篇文章:

我们可以点进去看一下,它其实说的就是 Network File System (NFS),我们往下阅读就可以看到这部分关于 ubuntu22.04 的:

从这里可以知道还有个 /etc/nfs.conf 文件,我们修改下这个文件:
sudo vim /etc/nfs.conf把文件汇总 vers2 = n 改为 vers2 = y,并把前面的#去掉:

然后我们重启 nfs 服务并查看一下当前系统支持的 nfs 协议版本:

发现这个时候,nfs2.0 协议被支持,然后我们重启开发板,就会正常挂载 nfs 中的根文件系统啦,此时我们的 uboot 相关环境变量如下所示:

不出意外的话,VFS 这里是可以加载成功的,但是又报错了:

后边这些错似乎是因为根文件系统,我后来换了自己以前移植的 busybox 根文件系统后就正常了,可以正常进入终端:

所以前边的方法是可以处理掉 nfs 协议不对的问题的。
2. 设备树名称问题
这个问题是最开始的学习的时候出现的,这里没更新相关内容,但是逻辑都是一样的。
2.1 问题复现
有时候可能会出现这样的问题 uboot 可以正常启动内核,但是显示完 Starting kernel ... 之后,就卡死了,不再有输出,然后我们还会看到有这么一个 error :

这是我以前遇到的一个问题,在这里也记录一下吧。我们其实可以将 ubuntu 中设备树的名字改掉,例如:

然后我们按之前的参数重启开发板:

就会发现,这里会提示找不到文件,但是系统还是正常起来了,以前遇到的卡死问题,倒是不知道为什么发生了,先不管,这里了解一下就行。
2.2 问题分析
其实很明显,出现这个错误的原因是因为 uboot 里面默认的设备树名字就是 imx6ull-14x14-emmc-4.3-800x480-c.dtb,但是呢,我们读取的地方并不存在这个文件,或者文件名不对的时候,就会出现这样的情况。
2.3 解决方法
2.3.1 解决方法 1
我们修改一下 boocmd 和 bootargs 环境变量的值:
=> setenv bootcmd 'tftp 80800000 /imx6ull/zImage\;tftp 83000000 /imx6ull/imx6ull-alpha-emmc.dtb\;bootz 80800000 - 83000000'
=> saveenv然后我们重启开发板,就会发现可以正常启动了:

2.3.2 解决方法 2
上边的解决办法在每次重新烧写系统以后都要先手动设置一下 bootcmd 的值,这样有点麻烦 ,当然还有一劳永逸的方法,那就是我们直接修改 uboot 源码中默认的环境变量值。打开 uboot 源码中 include/configs/mx6ull_alpha_emmc.h 文件,在宏 CONFIG_EXTRA_ENV_SETTINGS 中找到如下所示内容:
"findfdt="\
"if test $fdt_file = undefined; then " \
"if test $board_name = EVK && test $board_rev = 9X9; then " \
"setenv fdt_file imx6ull-9x9-evk.dtb; fi; " \
"if test $board_name = EVK && test $board_rev = 14X14; then " \
"setenv fdt_file imx6ull-14x14-evk.dtb; fi; " \
"if test $fdt_file = undefined; then " \
"echo WARNING: Could not determine dtb to use; fi; " \
"fi;\0" \findfdt 就是用于确定设备树文件名字的环境变量, fdt_file 环境变量保存着设备树文件名。第 3 行和 4 行用于判断设备树文件名字是否为 imx6ull-9x9-evk.dtb,第 5 行和 6 行用于判断设备树文件名字是否为 imx6ull-14x14-evk.dtb。这两个设备树都是 NXP 官方开发板使用的, I.MX6U-ALPHA 开发板用不到,因此直接将 findfdt 的值改为如下内容:
"findfdt="\
"if test $fdt_file = undefined; then " \
"setenv fdt_file imx6ull-alpha-emmc.dtb; " \
"fi;\0" \然后我们重新编译 uboot,替换掉之前的 uboot.imx 文件,然后重新烧写就可以啦,就可以达到与之前一样的效果啦。
