【移植04】u-boot(Bootloader的后两个阶段)、设备树文件(dtb)、内核镜像(zImage)、根文件系统从烧写存储到eMMC中到完整加载运行的过程与原理详解

目录

本篇博文我们要实现什么?

在前面的两篇博文中我们只是把移植修改生成的内核、设备树、根文件通过网络进行的加载,也就是并没有把它们写入到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环境变量的内容。

流程分析

  1. 倒计时阶段

    • 在U-Boot启动时,会显示倒计时,例如:
      Hit any key to stop autoboot:  3
      
    • 这个倒计时是由 autoboot_command() 相关代码控制的,主要作用是让用户有机会手动中断自动启动。
  2. 倒计时完成后

    • 如果用户没有按键中断,U-Boot 会执行:
      run_command(env_get("bootcmd"), 0);
      
    • 其中 env_get("bootcmd") 取出 bootcmd 环境变量的内容,并将其传递给 run_command() 执行。

那么如何查看环境变量 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
压缩文件解压后如下图所示:
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昊虹AI笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值