目录
- 本篇博文我们要实现什么?
- 读本篇博文所需要的先行知识
- 整个Bootloader的流程详解
- 百问网的烧写工具烧写镜像文件u-boot-dtb.imx的脚本分析
- 利用百问网的烧写工具将u-boot的镜像文件u-boot-dtb.imx烧写到eMMC中
- u-boot在倒计时完后,首先执行的是哪条命令?具体内容是什么?
- 详解u-boot倒计时完成后执行的首条命令相关的环境变量`bootcmd`
- 结合实际对u-boot的相关环境变量进行修改
- 利用以网络形式启动Linux系统将根文件系统写入到eMMC的`User Area`区的第2逻辑分区中
- 利用以网络形式启动Linux系统将内核镜像和设备树文件写到eMMC的`User Area`区的第2逻辑分区的目录/boot中
- 从eMMC启动Linux系统并正常运行
- 保存对环境变量的修改,让开发板能完全自启动Linux系统
- 测试能否挂载网络文件系统
- 通过网络文件系统将蓝牙的固件复制到相应的根文件目录中
- 通过网络文件系统将wifi的固件复制到相应的根文件目录中
- 通过网络文件系统将蓝牙和wifi的驱动模块复制到相应的根文件目录中
- 测试蓝牙模块是否能正常运行
- 测试wifi模块是否能正常运行
- 附相关文件
本篇博文我们要实现什么?
在前面的两篇博文中我们只是把移植修改生成的内核、设备树、根文件通过网络进行的加载,也就是并没有把它们写入到eMMC存储器,当最终修改测试没问题,显然我们应该是把这些东西烧写到eMMC存储器中的。
所以本博文的目标很明确:就是要把我们在前面两篇博文移植修改相关源码生成的u-boot、内核、设备树、根文件写到eMMC存储器中,从而实现从eMMC存储器启动一个完整并且能正常运行的Linux系统。
前面两篇博文是指下面这篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/145662136
https://blog.youkuaiyun.com/wenhao_ir/article/details/145822475
读本篇博文所需要的先行知识
关于芯片内部的ROM的作用、工作原理的介绍,链接如下:
https://blog.youkuaiyun.com/wenhao_ir/article/details/145969584
eMMC存储器详解(存储区域结构、EXT_CSD[179]、各分区介绍、主要引脚、命令格式与类型等)
https://blog.youkuaiyun.com/wenhao_ir/article/details/145967306
IMX6ULL运行Linux系统的Bootloader的三个阶段详解(BootROM、SPL、U-Boot)(SPL和U-Boot合成镜像u-boot-dtb.imx时需要作填充数据处理)
https://blog.youkuaiyun.com/wenhao_ir/article/details/145999721
百问网(100ask)提供的烧写工具的原理和详解;将自己编译生成的u-boot镜像文件烧写到eMMC中
https://blog.youkuaiyun.com/wenhao_ir/article/details/145653414
运行于u-boot中的Fastboot(FB)协议介绍(快速烧写 eMMC、NAND、UFS 等存储设备的协议)
https://blog.youkuaiyun.com/wenhao_ir/article/details/145985144
u-boot中与eMMC/SD卡相关的功能模块“MMC sub system”的详细介绍
https://blog.youkuaiyun.com/wenhao_ir/article/details/146016551
整个Bootloader的流程详解
如果要了解整个Linux系统的启动流程,首先要搞清楚Bootloader。
Bootloader分为三个部分,即处理器内部 ROM 代码(BootROM)、SPL(Secondary Program Loader)、U-Boot(完整 Bootloader)。
处理器内部 ROM 代码(BootROM)是写死在处理器的内部的,而SPL(Secondary Program Loader)、U-Boot(完整 Bootloader)这是可以由u-boot生成的,通常u-boot的镜像文件u-boot-dtb.imx就包含这两个阶段的代码。
整个流程的详解我已经写在了博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145999721 中,所以到那篇文章中去看吧。
搞清楚上面这篇博文中关于Bootloader的流程详解后,就要看包含Bootloader的后两个阶段的镜像文件u-boot-dtb.imx是如何烧写到eMMC中的,烧写到eMMC中后,又是怎么装载到内存中的,也就是下面一个目录的内容。
百问网的烧写工具烧写镜像文件u-boot-dtb.imx的脚本分析
百问网提供的烧写工具的相关脚本D:\Program Files\100ask_imx6ull_pro开发板系统烧写工具\scripts\basic\emmc\write_boot.clst的详细分析见博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/146012950 【搜索"包含SPL和U-Boot的镜像文件u-boot-dtb.imx烧写到eMMC存储器的脚本分析"】
从上面这篇博文对烧写脚本的分析可知:
镜像文件u-boot-dtb.imx是烧写到了 boot partition 1(boot1)中,而不是 boot partition 2(boot2)中的。并且镜像烧写工具在烧写完成后还把 boot partition 1(boot1)设置为了启动分区,即eMMC启动后,首先从boot partition 1(boot1)向内存发送SPL和U-Boot代码。
另外从博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145999721 中可以知道,镜像文件u-boot-dtb.imx中包含着Bootloader的后两个阶段,即SPL和U-Boot阶段。
利用百问网的烧写工具将u-boot的镜像文件u-boot-dtb.imx烧写到eMMC中
之前已在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145662136 将镜像文件u-boot-dtb.imx烧写到了eMMC中,详细的烧写过程请参考那篇博文。
镜像文件u-boot-dtb.imx烧写到eMMC中,把开发板的启动模式设置为eMMC启动后,再打开串口,启动开发板,就可以看到u-boot正常运行了。
接下来我们需要知道“ u-boot在倒计时完后,首先执行的是哪条命令?具体内容是什么?”
u-boot在倒计时完后,首先执行的是哪条命令?具体内容是什么?
现在我需要知道u-boot在启动后,倒计时完后准备加载内核和设备树,那么它倒计时完后首先执行的是哪条命令?
在U-Boot的启动流程中,倒计时结束后,U-Boot会执行环境变量中定义的启动命令。具体来说,它首先执行的是bootcmd环境变量的内容。
流程分析
-
倒计时阶段:
- 在U-Boot启动时,会显示倒计时,例如:
Hit any key to stop autoboot: 3 - 这个倒计时是由
autoboot_command()相关代码控制的,主要作用是让用户有机会手动中断自动启动。
- 在U-Boot启动时,会显示倒计时,例如:
-
倒计时完成后:
- 如果用户没有按键中断,U-Boot 会执行:
run_command(env_get("bootcmd"), 0); - 其中
env_get("bootcmd")取出bootcmd环境变量的内容,并将其传递给run_command()执行。
- 如果用户没有按键中断,U-Boot 会执行:
那么如何查看环境变量 bootcmd 的具体内容呢?
其实之前在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145902134 中已经详细叙述了方法,并且连具体的内容我都列出了。
环境变量 bootcmd 的内容如下:
bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi
分行和缩进处理后如下:
bootcmd=
run findfdt;
run findtee;
mmc dev ${mmcdev};
mmc dev ${mmcdev}; # 重复执行了一次,可能是为了确保正确切换到目标 mmc 设备
if mmc rescan; then
if run loadbootscript; then
run bootscript;
else
if run loadimage; then
run mmcboot;
else
run netboot;
fi;
fi;
else
run netboot;
fi;
接下来对它的内容进行详细分析。
详解u-boot倒计时完成后执行的首条命令相关的环境变量bootcmd
环境变量bootcmd的具体内容
关于u-boot倒计时完成后执行的首条命令run_command(env_get(“bootcmd”), 0);的具体内容存储于环境变量bootcmd中,所以我们要搞清楚把设备树文件、内核镜像、根文件系统烧写到eMMC的哪个位置,搞清楚内核的启动流程,就必须搞清楚环境变量bootcmd的内容。
环境变量 bootcmd 的内容如下:
bootcmd=run findfdt;run findtee;mmc dev ${mmcdev};mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi
分行和缩进处理后如下:
bootcmd=
run findfdt;
run findtee;
mmc dev ${mmcdev};
mmc dev ${mmcdev}; # 重复执行了一次,可能是为了确保正确切换到目标 mmc 设备
if mmc rescan; then
if run loadbootscript; then
run bootscript;
else
if run loadimage; then
run mmcboot;
else
run netboot;
fi;
fi;
else
run netboot;
fi;
接下来对它的内容进行详细分析。
环境变量bootcmd的解析及重要信息
关于环境变量bootcmd的详细解释见我的另一篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/146051313
关于环境变量bootcmd的详细解释见我的另一篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/146051313
环境变量bootcmd解析后得到的重要信息
关于内核镜像文件的说明
①内核镜像文件存储在eMMC的当前活动分区(通常为User Area区)的第1逻辑分区的根目录中,其文件名为zImage,加载到内存中的位置为:0x80800000位置,要求eMMC的当前活动分区(通常为User Area区)的第1逻辑分区的文件系统为FAT。
关于设备树文件的说明
②设置树文件存储在eMMC的当前活动分区(通常为User Area区)的第1逻辑分区的根目录中,其文件名为imx6ull-14x14-evk.dtb,加载到内存中的位置为:0x83000000位置,要求eMMC的当前活动分区(通常为User Area区)的第1逻辑分区的文件系统为FAT。
关于根文件系统挂载的说明
③根文件系统的挂载位置在设备节点/dev/mmcblk1p2,mmcblk1p2 的1代表eMMC设备的编号为1,p2代表用户数据区(User Area)的第2个逻辑分区。由于根文件系统的挂载是在内核启动后进行的,所以挂载位置的设备节点的名字是Linux系统中的名字。如何获取eMMC存储器各分区在Linux中的设备文件名,请参看我的另一篇博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/146088727 【搜索“在Linux中的分区名字和编号问题”】
结合实际对u-boot的相关环境变量进行修改
01-修改环境变量前要知道的重要事情
①在烧写u-boot的末尾,我们已经将eMMC存储器进入第二阶段(正常数据操作阶段)的访问分区设置为了eMMC存储器的User Area区,即eMMC存储器进入第二阶段(正常数据操作阶段)后的当前活动分区为User Area区。
②我们会将根文件系统存放于eMMC存储器的User Area区的第二逻辑分区,并且第二逻辑分区的文件系统格式为ext4。
③内核镜像文件zImage会存放于根文件系统的目录/boot下。
④设备树文件imx6ull-14x14-evk.dtb会存放于根文件系统的目录/boot下。
02-对环境变量mmcpart的修改
由于内核镜像文件zImage、设备树文件imx6ull-14x14-evk.dtb是存放于根文件系统的目录/boot下,所以这两个文件实际上是存放于User Area区的第二逻辑分区,所以我们需要将环境变量mmcpart的值由1改为2。
即像下面这样设置:
setenv mmcpart 2
03-对环境变量loadimage的修改
由于User Area区的第二逻辑分区的文件系统是ext4,而不是fat;、
再由于内核镜像文件并不存放于User Area区的第二逻辑分区的根目录,而是存放于/boot/下,
所以需要把境变量loadimage的值由:
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
改为:
loadimage=ext4load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${image}
相应的修改命令为:
setenv loadimage 'ext4load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${image}'
04-对环境变量loadfdt的修改
由于User Area区的第二逻辑分区的文件系统是ext4,而不是fat;、
再由于设备树文件并不存放于User Area区的第二逻辑分区的根目录,而是存放于/boot/下,
所以需要把境变量loadfdt的值由:
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
改为:
loadfdt=ext4load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}
相应的修改命令为:
setenv loadfdt 'ext4load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}'
05-增加环境变量bootdir
由于对环境变量loadimage的修改和对环境变量loadfdt的修改引入了环境变量bootdir,所以还需要新装置环境变量bootdir,其值为/boot
相应的命令为:
setenv bootdir '/boot'
06-环境变量mmcroot的值不需要修改,不需要修改!
环境变量mmcroot原来的值为:/dev/mmcblk1p2 rootwait rw,
从博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/146088727 【搜索“我整理一下输出信息如下”】,可以看出,用于存放根目录的User Area区的第2逻辑分区的设备文件名的确为:mmcblk1p2,所以不需要修改。
07-汇总所有环境变量修改命令
汇总所有环境变量修改的命令如下:
setenv mmcpart 2
setenv loadimage 'ext4load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${image}'
setenv loadfdt 'ext4load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}'
setenv bootdir '/boot'
08-保存对环境变量的修改
详情请在本博文中搜索“让开发板能完全自启动Linux系统”
利用以网络形式启动Linux系统将根文件系统写入到eMMC的User Area区的第2逻辑分区中
详细的操作步骤见我的另一篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/146122161
详细的操作步骤见我的另一篇博文:
https://blog.youkuaiyun.com/wenhao_ir/article/details/146122161
利用以网络形式启动Linux系统将内核镜像和设备树文件写到eMMC的User Area区的第2逻辑分区的目录/boot中
这个步骤是承接上一步的,即首先你要确保根文件系统已经写入到eMMC的User Area区的第2逻辑分区中了。另外还需要确保eMMC的User Area区的第2逻辑分区已经挂载到目录/mnt_p2上了。
然后我们把内核镜像文件zImage和设备树文件imx6ull-14x14-evk.dtb放到以网络形式启动的根文件系统的目录/home中:

在删除前,我们先查看下目录/mnt_p2/boot/中现有的文件:

可见,里面是有文件的,为了在后面的操作中确认是我们新复制进去的文件,我们不妨把目录/mnt_p2/boot/下的所有文件删除,执行下面的命令把目录/mnt_p2/boot/下的所有文件删除:
rm -rf /mnt_p2/boot/*

然后就没有任何文件了。
接着运行下面的命令把两个文件都复制到目录/mnt_p2/boot/中:
cp -r /home/zImage /mnt_p2/boot/
cp -r /home/imx6ull-14x14-evk.dtb /mnt_p2/boot/

从eMMC启动Linux系统并正常运行
重启系统,然后u-boot那里暂停u-boot的自动启动进程…

使用下面的命令修改默认的环境变量值:
setenv mmcpart 2
setenv loadimage 'ext4load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${image}'
setenv loadfdt 'ext4load mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${bootdir}/${fdt_file}'
setenv bootdir '/boot'
至于为什么我要修改这些环境变量值,本篇博文前面我已经说明了。

修改完成后我们可以打印出这些环境变量的值,看下是否修改成功了:
printenv mmcpart
printenv loadimage
printenv loadfdt
printenv bootdir

然后我们试着启动一下,执行下面的命令尝试执行u-boot自动启动系统时的第一条命令:
run bootcmd

完美启动系统,之前在博文中出现的下面两个错误都没有了:
[FAILED] Failed to start Network Time Synchronization.
[FAILED] Failed to start Login Service.
至于为什么没有的原因我在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145883835 分析了,详情请在博文https://blog.youkuaiyun.com/wenhao_ir/article/details/145883835 中搜索关键词“后来通过eMMC启动系统时这两个错误都没有了,原因分析如下”。
保存对环境变量的修改,让开发板能完全自启动Linux系统
修改环境变量后,可用下面的命令:
saveenv
保存修改,这样以后就不用再修改了。

然后按复位键重启系统,就能正常重启了…
测试能否挂载网络文件系统
此时的网络连接依然通过路由器,如下图所示:


我们不妨Ping一下Ubuntu系统看能不能Ping通:
ping 192.168.5.11

没问题,然后执行下面的命令挂载网络文件系统:
mount -t nfs -o nolock,vers=3 192.168.5.11:/home/book/nfs_rootfs /mnt

看下效果:

可见文件内容是我们Ubuntu中的挂载目录中的内容了:

通过网络文件系统将蓝牙的固件复制到相应的根文件目录中
从博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145883835 中我们知道需要把蓝牙的固件复制到目录/lib/firmware中。
先把把蓝牙的固件复制到NFS网络文件系统中:
蓝牙固件的百度网盘下载地址如下:
https://pan.baidu.com/s/1kjXfhJlhGZ4UNHEZb1B8iQ?pwd=hj5q

然后复制到开发板的目录/lib/firmware中。
cp -r /mnt/rtl_bt /lib/firmware/
复制完了之后检查一下
ls /lib/firmware/rtl_bt

通过网络文件系统将wifi的固件复制到相应的根文件目录中
从博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145883835 中我们知道需要把wifi的固件复制到目录/lib/firmware中。
wifi的固件的百度网盘下载地址如下:
https://pan.baidu.com/s/1g6bNJkYu1ujas_FOhNK8qA?pwd=qf6t
先复制到NFS网络文件的目录中:

然后通过NFS网络文件系统挂载目录复制到目录/lib/firmware中:
cp -r /mnt/rtlwifi /lib/firmware/
复制完了之后检查一下
ls /lib/firmware/rtlwifi

通过网络文件系统将蓝牙和wifi的驱动模块复制到相应的根文件目录中
生成蓝牙和wifi的驱动模块时对内核的配置和编译操作见博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145822475
现在通过网络文件系统将蓝牙和wifi的驱动模块复制到相应的根文件目录中
首先进入到内核源码的根目录中:
cd /home/book/mybuild/Linux-BSP_raw/linux-imx
然后将模块安装到NFS网络文件系统的目录中:
sudo make ARCH=arm INSTALL_MOD_PATH=/home/book/nfs_rootfs modules_install


网络文件系统的目录中就有相应的模块库了:




然后把复制到开发板的根文件系统中的目录5.4.47-00055-g5ec03d06f54e-dirty复制到/lib/modules中:
cp -r /mnt/lib/modules/5.4.47-00055-g5ec03d06f54e-dirty /lib/modules/

用下面的命令检查下复制成功没有:
ls /lib/modules/5.4.47-00055-g5ec03d06f54e-dirty

这就有了。
测试蓝牙模块是否能正常运行
按照博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145822475 中的方法测试即可,在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145822475 中搜索关键词“测试蓝牙驱动模块是否能正常运行”
最终的结果如下:

说明蓝牙模块没有问题。
测试wifi模块是否能正常运行
按照博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145822475 中的方法测试即可,在博文 https://blog.youkuaiyun.com/wenhao_ir/article/details/145822475 中搜索关键词“测试wifi驱动模块是否正常”
由于上面这篇博文中记录的是探索过程,所以我不妨把最终精简的过程记录到下面:
先加载驱动模块eeprom_93cx6.ko
cd /lib/modules/5.4.47-00055-g5ec03d06f54e-dirty/kernel/drivers/misc/eeprom/
insmod eeprom_93cx6.ko
然后加载wifi驱动模块rtl8187.ko
cd /lib/modules/5.4.47-00055-g5ec03d06f54e-dirty/kernel/drivers/net/wireless/realtek/rtl818x/rtl8187
insmod rtl8187.ko
然后打开wifi…
ifconfig wlan0 up
扫描附近的wifi
iw wlan0 scan | grep "SSID"

可见,也发现了我们家和邻居家的wifi…至此测试成功~
整个对u-boot和Linux的移植过程到此结束。
附相关文件
根文件系统的压缩文件
https://pan.baidu.com/s/1NUnuC9dzBFS9oOC0EhjarQ?pwd=qcyz
蓝牙固件、wifi固件、ko模块库
https://pan.baidu.com/s/1x-2gFZ1ZNJfrZ7poCEIKwA?pwd=g5wr
压缩文件解压后如下图所示:

1万+

被折叠的 条评论
为什么被折叠?



