uboot 的最终目的就是启动 Linux 内核,所以需要通过启动 Linux 内核来判断 uboot 移植是否成功。在启动 Linux 内核之前我们先来学习两个重要的环境变量 bootcmd 和 bootargs。
bootcmd 环境变量
bootcmd 保存着 uboot 默认命令,uboot 倒计时结束以后就会执行 bootcmd 中的命令。这些命令一般都是用来启动 Linux 内核的,比如读取 EMMC 或者 NAND Flash 中的 Linux 内核镜像文件和设备树文件到 DRAM 中,然后启动 Linux 内核。可以在 uboot 启动以后进入命令行设置 bootcmd 环境变量的值。如果 EMMC 或者 NAND 中没有保存 bootcmd 的值,那么 uboot 就会使用默认的值,板子第一次运行 uboot 的时候都会使用默认值来设置 bootcmd 环境变量。bootcmd 的默认值就是CONFIG_BOOTCOMMAND定义如下:
findfdt 是用来查找开发板对应的设备树文件(.dtb)
mmc dev ${mmcdev}用于切换 mmc 设备切换到 EMMC 上
命令 mmc rescan 扫描
看有没有 SD 卡或者 EMMC 存在,如果没有的话就直接跳执行 run netboot,netboot也是一个自定义的环境变量,这个变量是从网络启动 Linux 的。如果 mmc 设备存在的话就从mmc 设备启动。
loadbootscript 就是从 mmc1 的分区 1 中读取文件 boot.src 到 DRAM 的 0X80800000 处。但是 mmc1 的分区 1 中没有 boot.src 这个文件
loadimage 就是从 mmc1 的分区中读取 zImage 到内存的 0X80800000 处,而 mmc1的分区 1 中存在 zImage。
加载 linux 镜像文件 zImage 成功以后就运行环境变量 mmcboot
mmcboot运行环境变量 mmcargs,mmcargs 用来设置 bootargs,并从 mmc1 的分区 1 中读取 imx6ull-14x14-evk.dtb 文件并放到 0x83000000处。如果读取.dtb 文件成功的话那就调用命令 bootz 启动 linux。
经过分析,浓缩出来的仅仅是 4 行精华:
mmc dev 1 //切换到 EMMC
fatload mmc 1:1 0x80800000 zImage //读取 zImage 到 0x80800000 处
fatload mmc 1:1 0x83000000 imx6ull-14x14-evk.dtb //读取设备树到 0x83000000 处
bootz 0x80800000 - 0x83000000 //启动 Linux
bootargs环境变量
bootargs 保存着 uboot 传递给 Linux 内核的参数,bootargs 环境变量是由 mmcargs 设置的
mmcargs=setenv bootargs console= ttymxc0, 115200 root= /dev/mmcblk1p2 rootwait rw
1.console 用来设置 linux 终端(或者叫控制台),也就是通过什么设备来和 Linux 进行交互,是串口还是 LCD 屏幕?如果是串口的话应该是串口几等等。一般设置串口作为 Linux 终端,这样我们就可以在电脑上通过 SecureCRT 来和 linux 交互了。这里设置 console 为 ttymxc0,因为 linux启动以后 I.MX6ULL 的串口 1 在 linux 下的设备文件就是/dev/ttymxc0,在 Linux 下,一切皆文件。ttymxc0 后面有个“,115200”,这是设置串口的波特率。
2.root 用来设置根文件系统的位置,root=/dev/mmcblk1p2 用于指明根文件系统存放在mmcblk1 设备的分区 2 中。
3.rootwait 表示等待 mmc 设备初始化完成以后再挂载,rw 表示根文件系统是可以读写的。
uboot 启动 Linux 测试
两种启动 Linux 内核的方法:一种是直接从 EMMC 启动,一种是从网络启动。
从 EMMC 启动也就是将编译出来的 Linux 镜像文件 zImage 和设备树文件保存在 EMMC中,uboot 从 EMMC 中读取这两个文件并启动。
从网络启动 linux 系统的唯一目的就是为了调试,设置 linux 从网络启动,也就是将 linux 镜像文件和根文件系统都放到 Ubuntu 下某个指定的文件夹中,这样每次重新编译 linux 内核或者某个 linux 驱动以后只需要使用 cp 命令将其拷贝到这个指定的文件夹中即可,这样就不用需要频繁的烧写 EMMC,我们可以通过 nfs 或者 tftp 从 Ubuntu 中下载 zImage 和设备树文件。
简单总结一下 uboot 移植的过程:
①、不管是购买的开发板还是自己做的开发板,基本都是参考半导体厂商的 dmeo 板,而半导体厂商会在他们自己的开发板上移植好 uboot、linux kernel 和 rootfs 等,最终制作好 BSP包提供给用户。我们可以在官方提供的 BSP 包的基础上添加我们的板子,也就是俗称的移植。
②、我们购买的开发板或者自己做的板子一般都不会原封不动的照抄半导体厂商的 demo板,都会根据实际的情况来做修改,既然有修改就必然涉及到 uboot 下驱动的移植。
③、一般 uboot 中需要解决串口、NAND、EMMC 或 SD 卡、网络和 LCD 驱动,因为 uboot的主要目的就是启动 Linux 内核,所以不需要考虑太多的外设驱动。
④、在 uboot 中添加自己的板子信息,根据自己板子的实际情况来修改 uboot 中的驱动。
U-Boot 图形化配置
我们知道 uboot 可以通过 mx6ull_alientek_emmc_defconfig 来配置,或者通过文件 mx6ull_alientek_emmc.h 来配置 uboot。还有另外一种配置 uboot 的方法,就是图形化配置,uboot 或 Linux 内核可以通过输入“make menuconfig”来打开图形化配置界面,.config 文件前面已经说了,这个文件保存着 uboot 的配置项,使用 menuconfig 配置完 uboot 以后肯定要更新.config 文件。Kconfig文件是图形界面的描述文件,也就是描述界面应该有什么内容,很多目录下都会有 Kconfig 文件。而 menuconfig 是通过读取.config 文件来判断使能了哪些功能,目标 menuconfig 依赖 scripts/kconfig/mconf,因此 scripts/kconfig/mconf.c 这个文件会被编译,生成 mconf 这个可执行文件。也就是说 mconf 会调用 uboot 根目录下的 Kconfig 文件开始构建图形配置界面。
Kconfig 语法简介
- mainmenu 就是主菜单,也就是输入“make menuconfig”以后打开的默认界面
- 和 makefile 一样,Kconfig 也可以调用其他子目录中的 Kconfig 文件,调用方法如下:
source “xxx/Kconfig” //xxx 为具体的目录名,相对路径
这些子目录下的 Kconfig 文件在主菜单中生成各自的菜单项。 - menu/endmenu 条目;menu 用于生成菜单,endmenu 就是菜单结束标志,这两个一般是成对出现的。还有些子菜单是由顶层Kconfig 所调用的子 Kconfig 文件来创建的。
- config 条目就是菜单的具体配置项,.config 文件中的“CONFIG_xxx” (xxx 就是具体的配置项名字)就是 Kconfig 文件中 config 关键字后面的配置项名字加上“CONFIG_”前
缀。比如:使能了 LOCALVERSION_AUTO功能,那么就会在.config 文件中生成CONFIG_LOCALVERSION_AUTO
5 .depends on 和 select
“A depends on B”说明B选中之后A才能选中;
A select B 说明选中A之后,B也会被选中; - choice/endchoice 代码段定义了一组可选择项,将多个类似的配置项组合在一起,供用户单选或者多选。菜单是可选的,当选中这个菜单以后就可以进入到子选项中。
添加自定义菜单
图形化配置工具的主要工作就是在.config 下面生成前缀为“CONFIG_”的变量,我们就来学习一下如何添加自己的自定义菜单,自定义菜单要求如下:
①、在主界面中添加一个名为“My test menu”,此菜单内部有一个配置项。
②、配置项为“MY_TESTCONFIG”,此配置项处于菜单“My test menu”中。
③、配置项的为变量类型为 bool,默认值为 y。
④、配置项菜单名字为“This is my test config”