LV01-02-IMX6ULL-ALPHA开发板体验-05-从tftp和nfs启动系统

本文主要是体验正点原子I.MX6U-ALPHA开发板出厂系统——从TFTP加载linux镜像和设备树,从NFS加载根文件系统的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.6的64位版本(一开始使用的是16.04版本,后来进行了升级)
VMware® Workstation 17 Pro 17.0.0 build-20800274
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
https://elixir.bootlin.com/linux/latest/source 在线阅读linux kernel源码
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)

一、写在最前面的

前面我们已经可以将正点原子出厂的系统烧写到SD卡或者EMMC中了,但是在开发过程中我们是要不断修改镜像和设备树以及根文件系统的,这样调试的话每次都要重新烧写,就格外的麻烦,其实uboot是支持从tftp下载设备树和linux镜像的,然后支持从nfs挂载根文件系统,这一节我们就来试一试吧。

由于还未开始学习uboot和linux内核、根文件系统等的移植,所以先使用正点原子出厂系统,先熟悉操作流程。注意本节使用的ubuntu版本是20.04。版本如下:

1
2
3
4
5
6
7
8
sumu@sumu-virtual-machine:~/4nfs$ cat /proc/version
Linux version 5.15.0-119-generic (buildd@lcy02-amd64-068) (gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #129~20.04.1-Ubuntu SMP Wed Aug 7 13:07:13 UTC 2024
sumu@sumu-virtual-machine:~/4nfs$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS
Release: 20.04
Codename: focal

二、网络环境

首先在正点原子出厂系统下,根据《LV02-03-网络环境-01-网络开发环境搭建》、《LV02-03-网络环境-02-TFTP环境搭建》和《LV02-03-网络环境-03-NFS环境搭建》这三节的笔记搭建好网络环境。

1. Ubuntu

ubuntu的IP地址如下:

image-20240830071116981

所以ubuntu使用的ip为:

1
192.168.10.108

2. windows

windows以太网口的信息如下

image-20240830071234560
1
2
3
IPv4地址    192.168.10.100
IPv4子网掩码 255.255.255.0
IPv4默认网关 192.168.10.1

3. Linux开发板

这个其实也不需要,主要是uboot阶段会设置IP,但是这里记录一下吧:

image-20240830071804472

所以开发板起来之后使用的IP为:

1
192.168.10.109

三、文件准备

1. 需要哪些文件?

前面学习烧写系统的时候,我们会向开发板中烧写uboot、linux内核、还有设备树这几个主要的文件。uboot是我们要使用的,只能提前通过脚本或者mfgtool烧写到开发板中,这里主要是从tftp加载linx内核和设备树,从nfs挂载根文件系统。所以我们需要的是linux内核镜像zImage,根文件系统rootfs.tar.bz2以及设备树文件。根文件系统rootfs和linx内核文件很容易就找到了,但是用的是哪个设备树?(其实我们在设置开发板uboot参数的时候会指定设备树的名称,但是这里使用的是正点原子出厂系统做测试,就和出厂系统使用的保持一致吧)

image-20240830074206692

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

image-20240830074346769

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

image-20240830074505866

2. 获取文件

上面知道我们需要准备zImage、dtb和根文件系统,我们从正点原子出厂开发工具中拷贝出来,

image-20230731182205914

如下所示:

image-20240830075012052

将对应的文件拷贝到虚拟机中的ubuntu中,分别放在ubuntu的指定目录下,如下图:

image-20240830075400490

四、uboot下的操作

1. 进入uboot

要是使用自己移植的uboot的话,开机时会有如下提示:

image-20230731182402754

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

image-20240831063129685

2. 网络环境变量设置

由于我们使用的是正点原子出厂的uboot,所以网络的驱动是移植好的,我们可以直接进行相关网络参数的配置。为防止可能之前修改过环境变量而导致无法挂载,我们可以使用 env default -a 指令先清除环境变量。

1
=>env default -a;saveenv

网络参数配置如下

1
2
3
4
5
6
7
=>setenv ipaddr 192.168.10.109     # 开发板 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.108 # 服务器地址,也就是 Ubuntu 地址
=>saveenv # 保存环境变量
=>print ipaddr ethaddr gatewayip netmask serverip

配置完我们可以打印看一下这些环境变量的值对不对:

image-20240831063627248

然后我们ping一下ubuntu和windows:

1
2
=>ping 192.168.10.100  # windows
=>ping 192.168.10.108 # ubuntu20.04
image-20240831065316358

这样表示可以正常通信,这里可能会有几个个问题:

(1)那就是ubuntu若是使用的双网卡的话,它可能会ping失败,由于我之前是没有找到怎么放开发板默认与ubuntu的桥接网卡通信,所以这里我就直接把NAT网卡关掉了,但是吧,我后来使用的ubuntu20.04似乎并没有这个问题,上图中是ping通了的。

(2)和windows进行ping的时候也有时候ping不通,后来发现可能是因为我使用的是扩展坞扩展的网口的原因,所以这里就可以重启一下网口,可以使用win+r组合键调出运行框,然后输入以下命令:

1
ncpa.cpl

然后就会打开控制界面,我们可以双击扩展的以太网口,进行禁用,然后重新启动即可。

(3)uboot 阶段, 虚拟机 ping 开发板可能是没有反馈的,也可以不用虚拟机去 ping 开发板,但是我这里看起来是ping通了。

(4)这个MAC地址我是按文档上写的,其实好像随便写一个也行,比如我自己移植系统的时候测试用的b8:ae:1d:01:00:00

(5)首次设置完开发板的网络参数的时候可能会出现下面的问题,我以为是出厂系统有问题,但其实重启一下就可以了:

image-20240831065241349

3. 修改 bootcmd 和 bootargs

3.1 bootcmd

我们在uboot输入以下命令:

1
2
=> 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服务器目录下相关文件如下:

image-20240831065557292

这里在进行配置的时候 /home/sumu/3tftp 是作为根目录的,所以对于开发板来说,zImage 所在的目录为 /imx6ull-alpha/zImage。

注意:80800000 - 83000000中的-两边各有一个空格。

3.2 bootargs

1
2
3
4
5
6
7
8
9
10
=> 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 init=/linuxrc'
=> saveenv

# 也可以换行写,注意用\换行时前面要有一个空格
=> 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 \
init=/linuxrc'
=> saveenv

格式说明:

1
2
=> setenv bootargs 'console=开发板串口,波特率 root=挂载方式 nfsroot=虚拟机ubuntu的IP地址:虚拟机ubuntu中文件系统路径,proto=传输协议 读写权限 ip=开发板IP地址:虚拟机ubuntu的IP地址:网关地址:子网掩码::开发板网口:off init=/linuxrc'
=> saveenv

这里主要是设置nfs根文件系统的挂载目录,我们在ubuntu中,nfs目录下相关文件如下:

image-20240831065639933

这里配置的时候就直接配置完整的绝对路径即可。

image-20240831092758373

【注意】这个地方有个坑,当时用的自己移植的系统,所以加上了init=/linuxrc这一句,要是使用正点原子官方出厂系统测试的话,这里不能加这个,不然会有问题。

4. 重启uboot测试

接下来就是重启测试了,我们可直接按下复位键或者在uboot中输入 re ,再按下回车即可。

4.1 uboot启动

最开始就是uboot的启动,我们可以看到会有以下打印信息,这个图是之前自己移植的uboot的启动打印:

image-20230731184858116

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

image-20240831070713973

4.2 下载设备树和linux镜像

image-20240831070810863

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

4.3 启动linux镜像

接下来就是启动linux内核了:

image-20240831071152604

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

image-20240831071321539
点击查看自己移植的系统nFS挂载失败的现象

最后挂载失败是这样:

image-20230731185428305

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

image-20240831093853900

这个好像是根文件系统的问题,是什么原因呢?原来是自己埋的坑啊,是因为加了一句init=/linuxrc,去掉就可以了。

1
2
3
4
=> 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

修改之后就能挂载成功了,也能正常启动:

image-20240831095915924

5. 恢复原有启动方式

我们改了uboot的环境变量,怎么恢复默认?可以执行以下命令:

1
=>env default -a;saveenv

这样就会回复默认的参数了。

五、问题处理

1. ubuntu20.04 NFS版本问题

1.1 确认当前支持的nfs版本

1
sudo cat /proc/fs/nfsd/versions
image-20240831072012231

(1)如果版本信息中包括了+2,而uboot的nfs服务仍不可使用,则这个方式应该也还是无法解决问题,就需要查找其它资料了。

(2)如果版本信息中包括了-2,则ubuntu内核版本不需要修改,直接跳到 修改nfs配置文件就可以了。

(3)如果版本信息中,最低是+3,则需要先切换ubuntu的Linux内核。这是因为Linux的6.2以上版本内核,将不再支持nfs2。

1.2 切换Linux内核

1.2.1 查看当前存在的内核版本

使用命令:

1
uname -r

如果显示的版本是6.2以上,则必须先降低Linux版本,比如这里是6.5版本:

1
6.5.0-15-generic

使用命令查看当前存在的内核镜像:

1
dpkg --get-selections | grep linux-image
在这里插入图片描述

如果没有6.2以下版本,(后面要写着install)则需要安装内核镜像才行。就算是写着install,也建议按照下面的步骤再安装一次,因为可能image装了,但是headers之类的没装!

1.2.2 安装指定版本的Linux内核

  • (1)安装镜像
1
sudo apt-get install linux-image-5.19.0-50-generic
  • (2)安装内核头文件
1
sudo apt-get install linux-headers-5.19.0-50-generic
  • (3)安装内核模块
1
sudo apt-get install linux-modules-5.19.0-50-generic
  • (4)安装驱动
1
sudo apt-get install linux-modules-extra-5.4.0-99-generic    
  • (5)要是出现问题的话,修复命令如下:
1
sudo apt --fix-broken install
  • (6)检查是否安装成功
1
dpkg -l | grep 5.19.0-50-generic

如下为成功

在这里插入图片描述

1.2.3 修改启动配置,使用新的Linux内核启动

修改启动配置,打开文件:

1
sudo vim /etc/default/grub

找到GRUB_DEFAULT=0,修改为:

1
GRUB_DEFAULT=“Advanced options for Ubuntu>Ubuntu,with Linux 5.19.0-50-generic”

找到GRUB_TIMEOUT=0,修改为:

1
GRUB_TIMEOUT=20

找到GRUB_CMDLINE_LINUX_DEFAULT=“quiet splash”,修改为

1
GRUB_CMDLINE_LINUX_DEFAULT=“text”

保存退出文件。然后更新grub:

1
sudo update-grub

最后重启系统:

1
sudo reboot

1.2.4 开机选择合适的Linux内核版本

开机后会进入下面的页面,选择下面高亮这个进入:

在这里插入图片描述

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

在这里插入图片描述

启动完成后,再次使用命令查看linux内核信息:

1
uname -r

会发现版本切换成功:

在这里插入图片描述

保证Linux版本是 5.x 的就可以正常开启使用nfs2服务。

1.3 NFS配置

前面已经安装过nfs了,接下来我么直接开始配。

1.3.1 修改/etc/default/nfs-kernel-server文件

打开配置文件:

1
sudo vim /etc/default/nfs-kernel-server

修改下面几个地方,要是没有就直接添加就好了:

image-20240831085847054

不出意外的话,后面肯定出意外了,重启之后查询启动情况会报这些问题:

(1)--ver相关报错:

image-20240831092037495

(2)--udp相关报错:

image-20240831092136017

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

image-20240831091929874

1.3.2 修改/etc/hosts文件

打开hosts文件:

1
sudo vim /etc/hosts

添加开发板的ip和nfs服务器的地址:

image-20240831090140714

ip是开发板的ip,后面那个是nfs文件系统存放的绝对路径。

1.3.3 修改/etc/nfs.conf文件

打开文件:

1
sudo vim /etc/nfs.conf

这个文件在ubuntu20.04中好像没有,那就直接新建好了:

image-20240831090512467

1.4 重启nfs服务

1
2
sudo exportfs  -ar
sudo service nfs-kernel-server restart

执行下面的命令确认nfs服务开启:

1
netstat -a | grep "nfs"
image-20240831092241852

1.5 查看nfs版本号

1
sudo cat /proc/fs/nfsd/versions
image-20240831092332328

可以看到nfs版本中已经有了+2。然后我们重启开发板应该就可以正常挂载根文件系统了。

参考资料:

【嵌入式Linux开发】Ubuntu22.04切换Linux内核,启用nfs2,解决uboot无法通过nfs服务从Ubuntu下载系统镜像(TTTTTT)_ubuntu22.04切换kernel6.2-CSDN博客

2. ubuntu22.04 NFS版本问题

说明:这个问题是最开始的学习的时候用ubuntu22.04出现的,以及解决办法,后来用了20.04,看上一节笔记就是了,这里可以作为参考。

对于上边开发板无法从新版本的ubuntu挂载nfs上的根文件系统的问题,这里我们来看一下怎么处理。(这个问题比较顽固,而且由于后面ubuntu22.04下总是有各种问题,所以后来我就换回ubuntu16.04啦,这里拿来参考下好了)。

在网上查阅资料会发现,linux内核支持的nfs协议是2.0的,但是新版本的ubuntu默认是不支持nfs2.0的,我们可以看一下当前系统支持的nfs协议版本:

1
sudo cat /proc/fs/nfsd/versions
image-20230731192258556

可以看到,新版本的ubuntu是没有支持nfs2.0协议的,所以开发板和ubuntu通过nfs通信失败也是正常,那怎么处理?网上一搜就有很多资料,一般都是修改下边这个文件,添加nfs2.0协议:

1
sudo vim /etc/default/nfs-kernel-server

这个文件修改后的完成内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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服务:

1
sudo service nfs-kernel-server restart

然后再查看一下nfs的支持版本:

1
sudo cat /proc/fs/nfsd/versions
image-20230731193136229

发现并未生效,原因在哪呢?原因在于我们的系统实在太信了,所以还有其他的文件需要配置,哪个文件?我们可以去ubuntu官网搜索一下(Enterprise Open Source and Linux | Ubuntu)会发现这样一篇文章:

image-20230731192629843

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

image-20230731192747078

从这里可以知道还有个 /etc/nfs.conf 文件,我们修改下这个文件:

1
sudo vim /etc/nfs.conf

把文件汇总vers2=n改为vers2=y,并把前面的#去掉:

image-20230731193359465

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

image-20230731193446085

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

image-20230731195134292

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

image-20230731195712698

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

image-20230731200100507

所以前边的方法是可以处理掉nfs协议不对的问题的。

2. 设备树名称问题

这个问题是最开始的学习的时候出现的,这里没更新相关内容,但是逻辑都是一样的。

2.1 问题复现

有时候可能会出现这样的问题uboot可以正常启动内核,但是显示完 Starting kernel … 之后,就卡死了,不再有输出,然后我们还会看到有这么一个 error :

image-20221023160744688

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

image-20230731200750378

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

image-20230731201012558

就会发现,这里会提示找不到文件,但是系统还是正常起来了,以前遇到的卡死问题,倒是不知道为什么发生了,先不管,这里了解一下就行。

2.2 问题分析

其实很明显,出现这个错误的原因是因为 uboot 里面默认的设备树名字就是 imx6ull-14x14-emmc-4.3-800x480-c.dtb,但是呢,我们读取的地方并不存在这个文件,或者文件名不对的时候,就会出现这样的情况。

2.3 解决方法

2.3.1 解决方法1

我们修改一下 boocmd 和 bootargs 环境变量的值:

1
2
=> setenv bootcmd 'tftp 80800000 /imx6ull/zImage\;tftp 83000000 /imx6ull/imx6ull-alpha-emmc.dtb\;bootz 80800000 - 83000000'
=> saveenv

然后我们重启开发板,就会发现可以正常启动了:

image-20230731201429073

2.3.2 解决方法2

上边的解决办法在每次重新烧写系统以后都要先手动设置一下 bootcmd 的值,这样有点麻烦 ,当然还有一劳永逸的方法,那就是我们直接修改 uboot 源码中默认的环境变量值。打开 uboot 源码中 include/configs/mx6ull_alpha_emmc.h 文件,在宏 CONFIG_EXTRA_ENV_SETTINGS 中找到如下所示内容:

1
2
3
4
5
6
7
8
9
"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 的值改为如下内容:

1
2
3
4
"findfdt="\
"if test $fdt_file = undefined; then " \
"setenv fdt_file imx6ull-alpha-emmc.dtb; " \
"fi;\0" \

然后我们重新编译uboot,替换掉之前的uboot.imx文件,然后重新烧写就可以啦,就可以达到与之前一样的效果啦。