LV100-linux下烧写系统到TF卡
SD 卡 是 Secure Digital Card 的英文缩写,直译就是“安全数字卡”。一般用于数码相机等,做外存储器用。
TF 卡 即是 T-Flash 卡,又叫 micro SD 卡,即微型 SD 卡。TF 卡一般也是用于手机、mp4 等小型数码电子产品的外存储器用。
严格来说下面笔记中用的是 micro SD 卡,应该称谓 TF 卡,但是叫习惯了,叫 SD 卡应该也问题不大。
一、有哪些文件?
前面我们学习 linux 系统组件,知道它包括 uboot、kernel、设备树、根文件系统,我们前面做完移植,得到了下面这些文件。

前面我们将 uboot 的 imx 文件烧写到 tf 卡中,从 tftp 下载 zImage 和 dtb,从 nfs 挂载根文件系统,那么我们怎么脱离网络启动?我们从这一节开始来详细学习一下系统的烧写。
二、怎么给 SD 卡分区
首先,我们来学习一下 SD 卡分区,后面会知道为什么要先学习这个了。这里我们在 ubuntu 中操作。
1. 查看 SD 卡信息,确定挂载点
1.1 插入 sd 卡
首先我们要将 SD 卡插入 PC 电脑,保证 vmware 中的 ubuntu 识别到了 sd 卡,这个一般都没啥问题。
1.2 相关命令
fdisk -l # 查看系统真实存在的设备,也就是说,是一直处于动态监控设备状态,只要有一个设备‘消失’,它都会显示。
cat /proc/partitions # 系统已识别的设备
df -h注意: 查看系统真实存在的硬盘设备的时候
- 查看系统真实存在的硬盘设备,看到的不一定是系统识别的
- 存在的,不一定是识别的
- 识别的,不一定是使用的
- 使用的,不一定是正在用的
- 系统真实存在的不一定能被识别,能识别的不一定能被用,能用的不一定是正在用
1.3 确定挂载点
然后我们通过下面的命令查看一下 sd 信息:
sudo fdisk -l然后会有如下打印信息:

可以看到 SD 卡的扇区大小是 512 字节,挂载点是 sdc。sd 卡挂载之后的节点一般都是 sd 开头的,我们可以在/dev 下看到对应的节点:
ls /dev/sd*
还有一个命令,可以直接打印出系统识别到的设备:
cat /proc/partitions
反正最后能确定挂载点就可以了。
2. 卸载 SD 卡
在进行分区操作时,我们需要先卸载 SD 卡:
umount /dev/sdx上面是 sdc,所以就是:
umount /dev/sdc
要是没挂载就算了。
3. 分区操作
3.1 进入分区菜单
sudo fdisk /dev/sdc
3.2 m——查看命令菜单
m
3.3 p——查看当前 SD 卡状态
p
可以看到我这里 SD 卡有没有分区,在进行 SD 卡分区前,我们要删除所有分区。这个删除操作后面再说。
3.4 创建分区
n # 新建一个分区
p # 使该分区为主分区
1 # 输入分区号,这里使用 1
2048 # 输入第一个扇区大小 2048
+10M # 输入最后一个扇区大小,这里使用+10M,表示直接给该分区预留 10MB
然后我们再用 p 看一下 sd 卡当前状态:

可以看到这里产生了一个 sdc1 节点,大小为 10M,这就是我们刚才制作的 10M 的分区。
3.5 设置分区类型
上面已经创建好分区了,接下来就是设置分区的类型:
t # 设置分区类型
c #设置分区类型,至于这里为什么输入 c,可以在设置分区类型的时候,按 L 查看不同分区格式对应的命令。我这里选择 FAT32 格式,根据自己需求选择即可
# 分区类型设置完成后,就可以看到系统打印出了相关的提示信息
a # 设为引导分区
然后我们再查看一下分区的结果:

3.6 写入分区
前面我们的分区实际上还并未生效,我们要输入 w 命令才可以:
w # 写入分区
输入完成后会直接退出 fdisk 的菜单界面。
3.7 格式化分区
我们下来看一下我们分区好后的节点情况:
ls /dev/sd*
sudo mkfs.vfat -F 32 -n boot /dev/sdc1mkfs.vfat:这是一个用于创建 VFAT 文件系统的命令。VFAT 是 FAT 文件系统的一个变种,它支持长文件名,通常用于 USB 闪存驱动器、SD 卡等存储设备。尽管名为vfat,这个命令也可以用来创建标准的 FAT12、FAT16 和 FAT32 文件系统。-F 32:这个选项指定了要创建的 FAT 文件系统的类型。数字32表示创建一个 FAT32 文件系统。FAT32 支持更大的分区和文件大小,通常用于大于 2GB 的分区。-n boot:这个选项用来设置文件系统的卷标(volume label)。在这个例子中,卷标被设置为boot。卷标是一个简短的文本字符串,用于在文件系统中标识分区,通常在挂载点、文件浏览器等地方显示。/dev/sdc1:这是指定要格式化的分区的设备文件。在 Linux 系统中,硬盘和分区通常以/dev/sdX的形式表示,其中X是字母,表示设备(如sda表示第一个硬盘),数字(如1)表示分区号(如sda1表示第一个硬盘的第一个分区)。在这个例子中,/dev/sdc1表示第三个硬盘的第一个分区。

会发现这里报了个警告。
先了解一下了解一下簇的概念和扇区的区别:
簇和扇区不是属于一个范畴。簇是系统在硬盘上读写文件时的单位,是一个数据块。而扇区是硬盘划分的最小单位值,就是簇(数据块)占用的地方。
簇的大小大于扇区的大小。硬盘每簇的扇区数与硬盘的总容量大小有关。扇区是存储介质上可由硬件寻址的基本单位,x86 系统几乎总是定义 512 字节的扇区大小;簇是许多文件系统格式使用的可寻址数据块,簇的大小总是扇区大小的整数倍,且不同文件系统对于不同大小的卷(分区)会有不同的默认簇大小,比如 FAT32 对于 8GB 以下的分区,默认簇大小为 4KB,对于 8GB ~ 16GB 的分区,默认簇大小为 16 个扇区(8KB),NTFS 对于大于 2GB 的分区,默认簇大小为 8 个扇区(4KB)。 打个比方,我们(系统)要在仓库(仓库可视为硬盘)里存放一些书(数据)。我们先把书分门别类放到一些大纸箱(簇)里,然后放进仓库,纸箱的体积是根据我们仓库大小来决定的,而仓库始终划分成单位为 0.1 立方米的小格子(扇区),仓库大了,纸箱就大些,仓库小了,纸箱就小些。
制作 fat32 文件系统有个最小的簇的个数要求:这里扇区大小为 512bytes,经测试当簇大小等于 1 个扇区时,分区最少需要 40MB,当簇大小设为 2 个扇区时,分区至少需要 80MB,40M / 512 = 81920, 80M / 512 / 2 = 81920,计算出制作 fat32 的分区大小至少需要有 81920 个簇。
我后面重新创建了一个 40M 的分区,然后格式化就没问题了:

这里这个这个警告信息是在提醒我们,在使用 DOS 或 Windows 系统时,使用小写的标签(labels)可能会导致一些问题或无法正常工作。这通常发生在处理文件系统、批处理脚本、环境变量或其他需要区分大小写的场景中,但 DOS 和早期版本的 Windows(如 Windows 95, 98, ME)的文件系统通常是不区分大小写的,而它们的某些命令行工具和环境变量处理却可能是区分大小写的。这里其实不用关心。
3.8 删除分区
上面不是刚好报了警告,我们学习一下怎么删除分区:
d # 删除分区
有多个分区?这个会提示我们选择要删除的分区的:

3.9 挂载分区
格式化完成后我们需要将 SD 卡挂载到系统,直接点击系统边上的 U 盘图标(要是没有的话,直接拔掉重新插一下应该就可以了):
可以看到这里的卷标就是 boot,使我们刚才设置的。我们还可以用 df 命令看一下:
df -h
我们也可以手动挂载:
sudo mkdir -p /mnt/sdc1_tmp # mkdir 命令创建一个空目录,用于挂载 SD 卡上的文件系统。
sudo mount /dev/sdc1 /mnt/sdc1_tmp # 挂载分区
3.10 卸载分区
sudo umount <sdcard mount dir>三、SD 卡启动盘制作
1. 参考资料
NXP 为我们提供了一份文档,,这个其实位于 L4.1.15_2.0.0_LINUX_DOCS (nxp.com.cn) 这个压缩包内,我们下载下来解压就可以看到:
fsl-yocto-L4.1.15_2.0.0-ga
├── doc
│ ├── i.MX_BSP_Porting_Guide.pdf
│ ├── i.MX_Graphics_User's_Guide.pdf
│ ├── i.MX_Linux_Reference_Manual.pdf
│ ├── i.MX_Linux_Release_Notes.pdf
│ ├── i.MX_Linux_User's_Guide.pdf
│ └── i.MX_VPU_Application_Programming_Interface_Linux_Reference_Manual.pdf
├── i.MX_Yocto_Project_User's_Guide.pdf
└── README
1 directories, 8 files《i.MX_Linux_User's_Guide.pdf》里面有一章在介绍这个:

这里有一份在线的,i.MX Linux User's Guide,但是不是这个 4.15 版本,不过都差不多。
2. SD 卡分区情况说明
先来看一下 i.MX6ULL 使用 SD 卡启动时的分区情况,NXP 官方给的镜像布局结构如下所示(《i.MX Linux User's Guide》的 4.3 Preparing an SD/MMC card to boot ):

可以看到,上图将一张 SD 卡分成了三部分:
第一部分:扇区起始地址 0x400bytes(2),大小为 20478 个扇区,分区格式为原始格式,用于存放 uboot。0x400 bytes 转为 10 进制是 1024 bytes, 刚好等于 2 个扇区大小;这两个扇区是为了保存分区表的信息。所以从 0x400 地址开始存放 U-boot,括号中的 2 可以理解为从第 2 个扇区的起始地址开始,扇区编号从 0 开始数起。
第二部分:扇区起始地址 0xa0000 bytes(20480 个扇区),大小为 500M(1024000 个扇区),分区格式为 FAT,用于存放内核和设备树;
第三部分:扇区起始地址 0x25800000(1228800 个扇区),大小为 SD 卡剩余的空间,分区格式为 Ext3 或 Ext4,用于存放 rootfs,也就是文件系统。(注意:就这里其实有个坑的,我也不知道为啥,后面会说到这个地方,这里注意下)

由于第一部分用于存放 U-Boot,无文件系统格式的要求,所以我们对分区的创建从第二部分(第 20480 扇区所在的地址)开始就可以。拿到一张 SD 卡,首先将 SD 卡在 windows 下格式化成 FAT32 格式:

这里只有 40M 应该是因为我前面做实验的时候的第一个分区是 40M 的原因。
3. 对 SD 卡重新分区
3.1 确认节点名称
我这里重新插入 ubuntu 后节点是 sdc:

可以看到是没有其他分区的,但有时候拿到的 sd 卡可能有分区,就按后面的继续处理。有分区的是这样的:

3.2 卸载所有分区
当有其他分区存在的时候,我们需要卸载所有分区
ls /dev/sdc*
sudo umount /dev/sdcxx3.3 清空 SD 卡
一般来说清空前面的部分数据即可:
sudo dd if=/dev/zero of=/dev/sdc bs=1024 count=1024
3.4 两个分区
手册上 fdisk 的命令说明:
# sudo fdisk /dev/sdx
p # [lists the current partitions]
d # [to delete existing partitions. Repeat this until no unnecessary partitions are reported by the 'p' command to start fresh.]
n # [create a new partition]
p # [create a primary partition - use for both partitions]
1 # [the first partition]
20480 # [starting at offset sector]
1024000 # [size for the first partition to be used for the boot images]
p # [to check the partitions]
n
p
2
1228800 # [starting at offset sector, which leaves enough space for the kernel, the bootloader and its configuration data]
<enter> # [using the default value will create a partition that extends to the last sector of the media]
p # [to check the partitions]
w # [this writes the partition table to the media and fdisk exits]这里我不按 NXP 的文档来了,主要是出现了这个问题,相同的步骤我按照 NXP 的文档,最后我挂载 sdc2 的时候出现了这个问题:

我按照网上的说法用下面这个命令处理:
sudo fsck -y /dev/sdc2 # 出现 "结构需要清理" 说明磁盘文献问题, 需要使用这个命令进行检查修复但是最后没啥用,我甚至还怀疑是我 sd 卡有问题,然后我去试了正点原子教程的分区方式,就没问题,说明还是分区的问题。最后也没解决,不过,分区还是为了放文件,也没有必要那么严格的按照 NXP 文档来。后面我就按需要来了。
3.4.1 分区 1——64M
这个分区用于存放 kernel 和设备树文件,我们可以看一下这两个文件多大:

发现一个文件 8M,一个文件 27K,所以其实第一个分区根本没必要 500M,这里给 64M 吧:

3.4.2 分区 2——剩余部分

3.4.3 设置分区格式
这里其实在 NXP 的文档中并没有,不过我看正点原子脚本中有,就也加上了:

这里主要是设置一下分区 1 的格式就行。然后我们再设置一下分区 1 为启动分区(官方文档没有这一步,正点原子的脚本有,这里就一起加上了):

3.4.4 最终分区情况

3.4.5 写入分区
最后的最后一定要输入 w 命令写入分区,确认分区的修改:

3.5 格式化分区
# 设置 sdc1 分区格式为 FAT
sudo mkfs.vfat -F 32 -n "boot" /dev/sdc1 # sdc1 分区格式为 FAT
# 设置 sdc2 分区格式为 Ext3 或 Ext4 都可以
sudo mkfs.ext3 -F -L "rootfs" /dev/sdc2 # sdc2 分区格式为 Ext3
# 或者
sudo mkfs.ext4 -F -L "rootfs" /dev/sdc2 # dc2 分区格式为 Ext4
3.6 SD 卡分区情况分析
我们来看一下最终的 sd 卡分区情况:

此时 sd 卡分布如下图:

前面好像就留了大概 1M 来写 uboot,够吗?我们看一下 uboot 镜像文件的大小:

基本上够了,uboot 只是拿来引导一下启动 linux 内核,也不会增加什么额外的功能,这个大小差不多够了。
4. 烧写 uboot 到 sd 卡
这个和前面没什么区别,就是从偏移 1KB 的位置开始烧写 uboot 的 imx 文件即可:
sudo dd if=u-boot-dtb.imx of=/dev/sdc bs=1024 seek=1 conv=fsync
从上面我们对 SD 卡分区的时候知道,uboot 需要从第 0 分区的扇区地址 2 开始的,SD 卡中一个扇区占 512 字节,SD 卡偏移 1K 字节处开始保存 uboot。上面这条命令中,bs 设置 dd 命令的读写块为 1K 大小(这个数据可以任意设置,设置大一点读写就快),并且设置 seek = 1(单位为 KB),表示从 SD 卡/dev/sdb 开头偏移 1K 字节处开始写 u-boot-dtb.imx,正好就是写到了 u-boot 在 SD 卡中 0 区所在位置处,前面预留 1K 字节刚好 2 个扇区大小保存分区表信息。
为什么从 SD 卡 1K 位置处存放 uboot 而不是从 SD 卡头部开始?
这是 NXP 官方手册《i.MX Linux® User's Guide 》上写前面 1K 字节是保存分区表的信息,在 Rev. L4.1.15_2.0.0-ga, 10/2016 这个版本上没有,但是后面更新的文档都有说明了,例如这个:i.MX Linux User's Guide 的 4.3.4 Copying a bootloader image 一节中有如下内容:
5. 拷贝 kernel 和 dtbs
接下来就是把 kernel 和设备树拷贝到分区 1 中。
- 创建挂载点
mkdir -p ~/tmp/sd_sdc1- 挂载分区 1
sudo mount /dev/sdc1 ~/tmp/sd_sdc1 # (注意根据上面的分区这是 sdc1,存放 kernal image 和 dtbs)- 使用 cp 命令将 zImage 和*.dtb 文件拷贝进/media/sd_sdc1 目录
sudo cp zImage ~/tmp/sd_sdc1/
sudo cp imx6ull-alpha-emmc.dtb ~/tmp/sd_sdc1/
- 取消挂载点
sudo umount ~/tmp/sd_sdc1
rm -rf ~/tmp/sd_sdc1
6. 拷贝文件系统到 SD 卡
6.1 拷贝文件
和分区 1 的操作流程一样:
mkdir -p ~/tmp/sd_sdc2
sudo mount /dev/sdc2 ~/tmp/sd_sdc2
sudo cp rootfs.tar.bz2 ~/tmp/sd_sdc2/
6.2 解压根文件系统
sudo mkdir rootfs
sudo tar -jxvf rootfs.tar.bz2 -C rootfs/
cd rootfs/
sudo tar -xf rootfs.tar
可能是打包方式问题吧,需要解压两次,但感觉又不应该,算了就这样吧。
6.3 拷贝到顶层目录
sudo cp -a * ~/tmp/sd_sdc2其实吧,直接在挂载的目录下解压就可以了,没必要多此一举,之前是以为有什么问题,后面直接挂载目录解压就可以。

6.4 取消挂载
sudo umount /dev/sdc2
rm -rf ~/tmp/sd_sdc27. 总结
到此为止,sd 卡启动盘就算是做好了,我们已经在 sd 卡中烧写了 uboot、kernel、dtb 文件以及根文件系统:

四、从 SD 卡启动
1. 拨码开关设置
我们要先设置为 SD 卡启动:

2. uboot 相关命令
我们先复习几个命令,后面会用到的。
2.1 进入 uboot

2.2 EMMC 和 SD 卡操作
我们来熟悉一下 mmc 相关的命令。uboot 支持 EMMC 和 SD 卡,因此也要提供 EMMC 和 SD 卡的操作命令。一般认为 EMMC 和 SD 卡是同一个东西 。
=> mmc
mmc - MMC sub system
Usage:
mmc info - display info of the current MMC device
mmc read addr blk# cnt
mmc write addr blk# cnt
mmc erase blk# cnt
mmc rescan
mmc part - lists available partition on current mmc device
mmc dev [dev] [part] - show or set current mmc device [partition]
mmc list - lists available devices
mmc hwpartition [args...] - does hardware partitioning
arguments (sizes in 512-byte blocks):
[user [enh start cnt] [wrrel {on|off}]] - sets user data area attributes
[gp1|gp2|gp3|gp4 cnt [enh] [wrrel {on|off}]] - general purpose partition
[check|set|complete] - mode, complete set partitioning completed
WARNING: Partitioning is a write-once setting once it is set to complete.
Power cycling is required to initialize partitions after set to complete.
mmc bootbus dev boot_bus_width reset_boot_bus_width boot_mode
- Set the BOOT_BUS_WIDTH field of the specified device
mmc bootpart-resize <dev> <boot part size MB> <RPMB part size MB>
- Change sizes of boot and RPMB partitions of specified device
mmc partconf dev [boot_ack boot_partition partition_access]
- Show or change the bits of the PARTITION_CONFIG field of the specified device
mmc rst-function dev value
- Change the RST_n_FUNCTION field of the specified device
WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.
mmc setdsr <value> - set DSR register value可以看出, mmc 后面跟不同的参数可以实现不同的功能 :
| 命令 | 描述 |
|---|---|
| mmc info | 输出 MMC 设备信息 |
| mmc read | 读取 MMC 中的数据。 |
| mmc wirte | 向 MMC 设备写入数据。 |
| mmc rescan | 扫描 MMC 设备。 |
| mmc part | 列出 MMC 设备的分区。 |
| mmc dev | 切换 MMC 设备。 |
| mmc list | 列出当前有效的所有 MMC 设备。 |
| mmc hwpartition | 设置 MMC 设备的分区。 |
| mmc bootbus…… | 设置指定 MMC 设备的 BOOT_BUS_WIDTH 域的值。 |
| mmc bootpart…… | 设置指定 MMC 设备的 boot 和 RPMB 分区的大小。 |
| mmc partconf…… | 设置指定 MMC 设备的 PARTITION_CONFG 域的值。 |
| mmc rst | 复位 MMC 设备 |
| mmc setdsr | 设置 DSR 寄存器的值。 |
2.2.1 mmc rescan 命令
=> mmc rescan好像没见打印输出,先不管。
2.2.2 mmc list 命令
list 命令用于来查看当前开发板一共有几个 MMC 设备,输入“mmc list” :
=> mmc list
FSL_SDHC: 0 (SD)
FSL_SDHC: 1可以看出当前开发板有两个 MMC 设备: FSL_SDHC: 0 和 FSL_SDHC: 1 (eMMC),这是因为我现在用的是 EMMC 版本的核心板,加上 SD 卡一共有两个 MMC 设备, FSL_SDHC: 0 是 SD 卡, FSL_SDHC: 1(eMMC)是 EMMC。
2.2.3 mmc dev 命令
mmc dev 命令用于切换当前 MMC 设备,命令格式如下
mmc dev [dev] [part]dev 用来设置要切换的 MMC 设备号, part 是分区号。如果不写分区号的话默认为分区 0。使用如下命令切换到 SD 卡:
=> mmc dev 0 # 切换到 SD 卡, 0 为 SD 卡, 1 为 eMMC
switch to partitions #0, OK
mmc0 is current device2.2.4 mmc info 命令
mmc info 命令用于输出当前选中的 mmc info 设备的信息,输入命令“mmc info”即可:
=> mmc info
Device: FSL_SDHC
Manufacturer ID: 99
OEM: 8888
Name: W620X
Bus Speed: 50000000
Mode : SD High Speed (50MHz)
Rd Block Len: 512
SD version 1.10
High Capacity: No
Capacity: 1.9 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes看出当前 SD 卡为 1.10 版本的,容量为 1.9GiB(2GB 的 SD 卡), 4 位宽的总线。
2.2.5 mmc part 命令
SD 卡或者 EMMC 会有多个分区,可以使用命令“mmc part”来查看其分区
- 查看 emmc 分区
=> mmc dev 1
Card did not respond to voltage select!这里好像切换失败了,先不管吧,后面再说。
- 查看 sd 卡分区
=> mmc dev 0
switch to partitions #0, OK
mmc0 is current device
=> mmc part
Partition Map for MMC device 0 -- Partition Type: DOS
Part Start Sector Num Sectors UUID Type
1 2048 131072 0acdccb3-01 0c Boot
2 133120 3798016 0acdccb3-02 83此时 SD 卡 有两个分区, 第一个分区起始扇区为 2048,长度为 131072 个扇区; 第二个分区起始扇区为 133120,长度为 3798016 个扇区。如果 SD 卡 里面烧写了 Linux 系统的话, SD 是有 3 个分区的,第 0 个分区存放 uboot,第 1 个分区存放 Linux 镜像文件和设备树,第 2 个分区存放根文件系统。但是在上面只有两个分区,那是因为第 0 个分区没有格式化,所以识别不出来,实际上第 0 个分区是存在的。一个新的 SD 卡默认只有一个分区,那就是分区 0,前面我们就是把 uboot 烧写到了分区 0.
2.3 FAT 格式文件系统操作
有时候需要在 uboot 中对 SD 卡或者 EMMC 中存储的文件进行操作,这时候就要用到文件操作命令,跟文件操作相关的命令有: fatinfo、 fatls、 fstype、 fatload 和 fatwrite,但是这些文件操作命令只支持 FAT 格式的文件系统!!
2.3.1 fatinfo 命令
fatinfo 命令用于查询指定 MMC 设备分区的文件系统信息 :
fatinfo <interface> [<dev[:part]>]interface 表示接口,比如 mmc, dev 是查询的设备号, part 是要查询的分区。比如我们要查询 SD 卡 分区 1 的文件系统信息 :
=> fatinfo mmc 0:1
Interface: MMC
Device 0: Vendor: Man 000099 Snr 00000700 Rev: 0.0 Prod: W620X
Type: Removable Hard Disk
Capacity: 1919.5 MB = 1.8 GB (3931136 x 512)
Filesystem: FAT32 "boot "2.3.2 fatls 命令
atls 命令用于查询 FAT 格式设备的目录和文件信息 :
fatls <interface> [<dev[:part]>] [directory]interface 是要查询的接口,比如 mmc, dev 是要查询的设备号, part 是要查询的分区, directory 是要查询的目录。比如查询 SD 卡分区 1 中的所有的目录和文件,输入命令:
=> fatls mmc 0:1
8315704 zImage
27436 imx6ull-alpha-emmc.dtb
2 file(s), 0 dir(s)可以看到分区 1 中就是 zImage 和 dtb 文件。
2.3.3 fstype 命令
fstype 用于查看 MMC 设备某个分区的文件系统格式 :
fstype <interface> <dev>:<part>我们来看一下 SD 卡的几个分区:
=> fstype mmc 0:0
** Unrecognized filesystem type **
=> fstype mmc 0:1
fat
=> fstype mmc 0:2
ext4
=> fstype mmc 0:4
** Invalid partition 4 **分区 0 格式未知,因为分区 0 存放的 uboot,并且分区 0 没有格式化,所以文件系统格式未知。分区 1 的格式为 fat,分区 1 用于存放 linux 镜像和设备树。分区 2 的格式为 ext4,用于存放 Linux 的根文件系统(rootfs)。
2.3.4 fatload 命令
fatload 命令用于将指定的文件读取到 DRAM 中 :
fatload <interface> [<dev[:part]> [<addr> [<filename> [bytes [pos]]]]]interface 为接口,比如 mmc, dev 是设备号, part 是分区, addr 是保存在 DRAM 中的起始地址, filename 是要读取的文件名字。 bytes 表示读取多少字节的数据,如果 bytes 为 0 或者省略的话表示读取整个文件。 pos 是要读的文件相对于文件首地址的偏移,如果为 0 或者省略的话表示从文件首地址开始读取。我们将 SD 卡分区 1 中的 zImage 文件读取到 DRAM 中的 0X80800000 地址处,
=> fatload mmc 0:1 80800000 zImage
8315704 bytes read in 794 ms (10 MiB/s)2.3.5 fatwrite 命令
注意! uboot 默认没有使能 fatwrite 命令,需要修改板子配置头文件,比如 mx6ullevk.h、mx6ull_alpha_emmc.h 等等,板子不同,其配置头文件也不同。找到自己开发板对应的配置头文件然后添加如下一行宏定义来使能 fatwrite 命令:
#define CONFIG_FAT_WRITE /* 使能 fatwrite 命令 */我用的这个 2019 版本的直接打开了好像,没打开的话在源码修改一下就可以了。fatwirte 命令用于将 DRAM 中的数据写入到 MMC 设备中,命令格式如下:
fatwrite <interface> <dev[:part]> <addr> <filename> <bytes>interface 为接口,比如 mmc, dev 是设备号, part 是分区, addr 是要写入的数据在 DRAM 中的起始地址, filename 是写入的数据文件名字, bytes 表示要写入多少字节的数据。我们可以通过 fatwrite 命令在 uboot 中更新 linux 镜像文件和设备树。
所以我们可以在 uboot 下在线更新 kernel 镜像文件和设备树文件,我们可以直接从 tftp 下载这两个文件到内存中,然后写入到对应的分区中去。
我们以更新 linux 镜像文件 zImage 为例,首先将 zImage 镜像文件拷贝到 Ubuntu 中的 tftpboot 目录下,命令 tftp 将 zImage 下载到 DRAM 的 0X80800000 地址处,命令如下:
=> tftp 80800000 zImage
ethernet@020b4000 Waiting for PHY auto negotiation to complete.... done
Using ethernet@020b4000 device
TFTP from server 192.168.10.101; our IP address is 192.168.10.102
Filename 'zImage'.
Load address: 0x80800000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
###############################################
972.7 KiB/s
done
Bytes transferred = 8315704 (7ee338 hex)zImage 大小为 8315704(0x7ee338)个字节 , 接下来使用命令 fatwrite 将其写入到 SD 卡的分区 1 中,文件名字为 zImage :
=> fatwrite mmc 0:1 80800000 zImage 7ee338
8315704 bytes written3. 手动启动内核
3.1 进入 uboot 界面
跟前面操作一样。
3.2 设置 bootargs 参数
设置 bootargs 参数,指定根文件系统保存的路径:
#指定根文件系统在 mmc 设备 0 的 2 分区上。并且 ip 地址为静态 ip 地址
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk0p2 ip=192.168.10.102:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc'
=> saveenv
Saving Environment to MMC... Writing to MMC(0)... OK这里主要是设置根文件系统的挂载路径,需要知道的是,不管是 sd 卡还是 emmc 在 linux 中的文件节点都叫 mmcblk,这里的 mmc0 就表示 sd 卡,p2 就表示 sd 卡的第二个分区,也就是我们前面创建的根文件系统所在分区。
3.3 加载内核镜像
加载 zImage 内核到内存的 0x80800000 地址上:
=> load mmc 0:1 0x80800000 zImage
8315704 bytes read in 800 ms (9.9 MiB/s)其实和前面的 fatload 是一样的。
3.4 加载设备树
加载设备树到 0x83000000 内存地址上:
=> load mmc 0:1 0x83000000 imx6ull-alpha-emmc.dtb3.5 启动内核
=> bootz 0x80800000 - 0x83000000
Kernel image @ 0x80800000 [ 0x000000 - 0x7ee338 ]
## Flattened Device Tree blob at 83000000
Booting using the fdt blob at 0x83000000
Using Device Tree in place at 83000000, end 83009b2b
ft_system_setup for mx6
Starting kernel ...
# ......然后内核就起来了,然后就出问题了:

4. 问题解决
4.1 JBD2——问题一
4.1.1 问题分析
我们先来看这两行:
[ 6.761505] JBD2: no valid journal superblock found
[ 6.766840] EXT4-fs (mmcblk0p2): error loading journal这里吧其实我没怎么深入研究,大概的原因就是新的操作系统上使用系统自带的 mkfs.ext4 对文件系统进行了格式化,默认使用了一些新的的特性,这些新的特性在旧的系统上是无法使用的,即在旧的内核上不支持。
我们可以看一下 ubuntu 的内核版本:

而我使用的 linux 内核是 4.19 版本的,也许是中间加入了某些新的特性吧。这里主要是 journal 相关的这个特性出了问题,怎么处理呢?网上搜了一堆,简单说就是把这个属性去掉就好了。
4.1.2 解决方法
- (1)确定 sd 卡节点
这里出问题的是 sd 卡的 ext4 分区,也就是 sdx2,我的挂载上来就是 sdc2:
sumu@sumu-virtual-machine:~/7Linux/imx6ull-alpha-release/release$ cat /proc/partitions
major minor #blocks name
#......
8 32 1965568 sdc
8 33 65536 sdc1
8 34 1899008 sdc2- (2)看分区 journal 日志
sudo tune2fs -l /dev/sdc2 | grep feature
发现是有这个 has_journal 特性,我们接下来要做的就是去掉这个特性。
- (3)卸载分区
sudo mount /dev/sdc2- (4)修改分区 journal 日志
sudo tune2fs -O ^has_journal /dev/sdc2
- (5)检查是否成功
sudo tune2fs -l /dev/sdc2 | grep feature
发现已经没有了。
4.1.3 开发板验证
我们来验证一下,和前面的步骤一摸一样,正常应该是问题已经解决了:

发现可以这次正常挂载了
4.2 EXT2-fs——问题二
甚至第二个问题也没了,大概应该是上面的问题一引起的第二个问题吧。那这里就不管了。
5. 自动挂载
每次都要敲那么多的命令,其实 uboot 是有自动运行的命令的,就是 bootcmd 环境变量,在 uboot 启动后,会自动运行这个环境变量里面的命令。我们来配置一下:
# 设置启动参数
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk0p2 ip=192.168.10.102:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc'
# 设置命令
=> setenv bootcmd 'mmc dev 0;fatload mmc 0:1 80800000 zImage;fatload mmc 0:1 83000000 imx6ull-alpha-emmc.dtb;bootz 80800000 - 83000000'
# 保存环境变量
=> saveenv
Saving Environment to MMC... Writing to MMC(0)... OK然后我们复位开发板就会发现可以自动加载镜像和设备树,并且自动启动了:

最后也是可以正常启动了:

五、做成 shell 脚本
每次敲那么多命令肯定很烦,做成 shell 脚本呗:release/sd_setup.sh · linux-dev-org/imx6ull-rootfs
参考资料:
SD 卡启动与 uboot 、zImage、dtb、rootfs 固化_怎么让 uboot kernel rootfs 在 sd 卡里运行-CSDN 博客
imx6ul 基于 zImage,dtb 在 SD 卡制作 linux 镜像 - 知乎 (zhihu.com)
imx6ull 开发板设置 SD 卡启动,SD 卡中烧写 uboot, kernel, 设备树, 根文件系统 fs_imx6ull sd 卡-CSDN 博客
解决 EXT4 使用无法挂载-阿里云开发者社区 (aliyun.com)
