前言
新冠疫情期间在家里无事可做,奈何手上没有现成的开发板,又想调试学习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
9init= [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 |