前言
新冠疫情期间在家里无事可做,奈何手上没有现成的开发板,又想调试学习linux内核,于是就有了这一系列的文章。本系列文章包含以下内容:
- 在家学习嵌入式1-搭建qemu环境
- 在家学习嵌入式2-在qemu环境下使用uboot启动linux
- 在家学习嵌入式3-使用Buildroot构建编译系统
- 在家学习嵌入式4-Versatile Express开发板
- 在家学习嵌入式5-buildroot的使用
在前面的文章中,我们简单的编译一个kernel,然后使用qemu-system-arm -kernel指定启动一个特定的linux kernel,在虚拟开发板上可以这么做。但是在实际的开发板中,启动是一个很复杂的东西。在启动kernel之前,通常有个小的引导程序(本文使用u-boot),引导程序需要事先存储在某些存储介质上,比如SD、eMMC、Flash、USB。当启动的时候,会有RomCode去初始化某些关键外设并且拷贝引导程序到DDR上。现在某些SOC支持ATF和TEE的功能,启动会变得更加的复杂。本文不讨论启动流程,本文只介绍在qemu环境下使用uboot通过sd卡去启动linux。
下载编译启动uboot
| 1 | git clone https://github.com/u-boot/u-boot.git -b v2017.01 | 
编译完毕之后,在当前目录下会生成u-boot文件,这个文件可以使用以下命令启动:
| 1 | qemu-system-arm -M vexpress-a9 -m 512M -kernel /home/victor/work/uboot/u-boot/u-boot -nographic -no-reboot | 
启动后,可以看到u-boot已经正常运行,但是由于找不到kernel,所以无法启动kernel,因此停留在u-boot命令行模式下。后续我们将基于这个版本修改uboot从sd卡启动linux kernel。
| 1 | victor@victor-linux:~$ qemu-system-arm -M vexpress-a9 -m 512M -kernel ~/work/uboot/u-boot/u-boot -nographic -no-reboot | 
制作sd卡
从uboot启动的log来看,好像是通过TFTP去load kernel但是找不到,因此就停留在uboot命令行模式。qemu支持模拟sd卡,因此,我们可以制作一个sd卡,然后将kernel、rootfs、dtb文件放到sd卡,在uboot中将sd卡中的文件load到DDR,并从DDR去启动,以下命令可以用于制作vfat文件系统的sd卡,大小为512M。
| 1 | dd if=/dev/zero of=/home/victor/work/sd_card bs=1M count=512 | 
为该sd卡创建分区,按o,n,w,创建完分区之后,然后使用sudo mkfs.vfat sd_card命令创建vfat文件系统。至此创建sd卡成功,查看该文件格式为:
| 1 | victor@victor-linux:~/work$ file sd_card | 
这部分的内容可以参考:ubuntu下格式化U盘
制作uramdisk.img
在之前的文章中,我们通过busybox制作了带ext4文件系统的a9rootfs.ext4文件,这个文件uboot是无法引导的,因此需要重新制作一个gzip和cpio压缩的uramdisk.img文件供uboot引导使用。
之前在制作a9rootfs.ext4的时候,我们创建过一个rootfs/目录用来存放根文件系统,现在可以使用这个系统制作ramdisk.img文件。
| 1 | cd /home/victor/work/linux/rootfs/ | 
但是这还不够,经过测试,如果使用ramdisk.img,那么使用bootz命令去启动的时候,会提示Wrong Ramdisk Image Format的错误,很明显是ramdisk.img格式不对。必须给ramdisk.img加上64字节的头部信息使之成为uramdisk.img文件,才能通过bootz命令去启动。这一步完成之后,我们可以得到uramdisk.img。
| 1 | victor@victor-linux:~/work/linux$ mkimage -A arm -O linux -C none -T ramdisk -a 0x00000000 -e 0x00000000 -n "wowothink Root Filesystem" -d /home/victor/work/linux/ramdisk.img /home/victor/work/linux/uramdisk.img | 
关于这部分的内容可以参考:解压打包img文件
拷贝文件到sd卡
连同之前编译linux kernel得到的zImage和vexpress-v2p-ca9.dtb,我们有3个文件需要拷贝到sd卡里面。将linux kernel的镜像zImage,rootfs的镜像uramdisk.img,dtb vexpress-v2p-ca9.dtb拷贝到sd卡里面。
| 1 | mkdir /home/victor/work/card/ | 
修改uboot从sd卡启动
在uboot v2017.01的版本上打入以下patch,以下patch主要修改的内容为:
- 设置启动delay为0s;
- 删除从TFTP启动的命令distro_bootcmd;
- 设置zImage、dtb、ramdisk的文件名字以及启动地址;
- 设置 - bootargs为- rdinit=/sbin/init console=${console},这里的- bootargs就是传递给kernel的cmdline。注意:由于我们制作的- uramdisk.img是根据busybox制作的,它的- init程序默认在- sbin/目录下,因此必须用- rdinit=来指定,不能使用- init=来指定。cmdline的- rdinit=和- init=的定义如下:- The kernel's command-line parameters - 1 
 2
 3
 4
 5
 6
 7
 8
 9- init= [KNL] 
 Format: <full_path>
 Run specified binary instead of /sbin/init as init
 process.
 rdinit= [KNL]
 Format: <full_path>
 Run specified binary instead of /init from the ramdisk,
 used for early userspace startup. See initrd.
- 使用 - fatload命令将镜像文件从sd卡拷贝到DDR上指定地址;
- 使用bootz启动。
| 1 | diff --git a/configs/vexpress_ca9x4_defconfig b/configs/vexpress_ca9x4_defconfig | 
启动带sd卡的qemu
qemu命令加上-sd的参数后,让该开发板支持sd卡,并且我们之前在uboot中指定从sd卡拷贝镜像到DDR中,并且启动kernel,可以进入串口。至此,在qemu环境下使用uboot通过sd卡去启动linux已经完成,我们可以通过这个过程学习uboot引导启动linux kernel的相关流程。
| 1 | qemu-system-arm -M vexpress-a9 -m 1024M -kernel /home/victor/work/uboot/u-boot/u-boot -nographic -no-reboot -sd /home/victor/work/sd_card | 
| 1 | victor@victor-linux:~$ qemu-system-arm -M vexpress-a9 -m 1024M -kernel /home/victor/work/uboot/u-boot/u-boot -nographic -no-reboot -sd /home/victor/work/sd_card |