为了能够更好的学习嵌入式Linux内核,本文基于qemu-system-arm工具模拟ARM公司的VersatileExpress硬件环境,基于buildroot工具制作rootfs,并且扩展了buildroot,增加内核配置管理,定制rootfs等功能。
我的上一篇文章,基于Buildroot的rootfs制作,讲述了如何基于Buildroot制作一个rootfs,所以,本文对于该部分进行了省略,有需要的请移步到这篇文章。
Ubuntu安装qemu比较简单,一条命令搞定.
$ sudo apt install qemu libncurses5-dev build-essential
$sudo apt install git
$mkdir ~/work/Linux/LinuxVersatile
$ git init
由于buildroot也是基于Git管理的,所以我们使用git submodule完成对buildroot的管理.
$ git submodule add git://git.busybox.net/buildroot
更新buildroot
$ git submodule update --init
一般情况下,对于一个特定的项目,我们要做的可能包括如下几方面:
配置buildroot(比如,编译选项,toolchain,bootloader,kernel,根文件系统以及image的类型等).
配置其他组件,比如linux内核、bootloader以及busybox.
定制目标根文件系统.
覆盖目标文件系统的某些文件(使用BR2_ROOTFS_OVERLAY).
修改或者删除目标文件系统中的某些文件,或者执行默认shell命令(使用BR2_ROOTFS_POST_BUILD_SCRIPT).
在生成images文件之前,执行任何命令(使用BR2_ROOTFS_POST_BUILD_SCRIPT).
设置文件的访问权限和所有者(使用BR2_ROOTFS_DEVICE_TABLE).
增加特定的设备文件(使用BR2_ROOTFS_STATIC_DEVICE_TABLE).
增加特定的用户(使用BR2_ROOTFS_USERS_TABLES).
在生成文件系统images之后,执行任何命令(使用BR2_ROOTFS_POST_IMAGE_SCRIPT).
为一些组件增加特定项目需要的补丁(使用BR2_GLOBAL_PATCH_DIR).
增加特定项目需要的组件.
增加特定项目需要的组件.
理论上,基于buildroot构建一个项目时,可以创建任何文件和目录。buildroot开发者推荐了下面的文件结构,我们可以在buildroot主目录里面创建这些文件,也可以在通过br2-external tree在
buildroot主目录之外创建下面的目录树.
+-- board/
| +-- <company>/
| +-- <boardname>/
| +-- linux.config
| +-- busybox.config
| +-- <other configuration files>
| +-- post_build.sh
| +-- post_image.sh
| +-- rootfs_overlay/
| | +-- etc/
| | +-- <some file>
| +-- patches/
| +-- foo/
| | +-- <some patch>
| +-- libbar/
| +-- <some other patches>
|
+-- configs/
| +-- <boardname>_defconfig
|
+-- package/
| +-- <company>/
| +-- Config.in (if not using a br2-external tree)
| +-- <company>.mk (if not using a br2-external tree)
| +-- package1/
| | +-- Config.in
| | +-- package1.mk
| +-- package2/
| +-- Config.in
| +-- package2.mk
|
+-- Config.in (if using a br2-external tree)
+-- external.mk (if using a br2-external tree)
+-- external.desc (if using a br2-external tree)
本文基于上述目录树结构来i构建项目,并且使用br2-extern tree这种构建特定项目的模式。需要的注意的是,如果我们选择br2-extern tree模式,上述文件结构中的company和boardname显得就有些多余了.
面一节介绍了如何构建特定项目的目录树,并且指定本项目使用br2-extern tree模式,下面介绍一下如何创建该目录树,并且说明一下如何实现相关的配置文件。需要思考的一个问题是,特定的项目
如何与buildroot进行集成呢?其实很简单,我们只需通过 BR2_EXTERNAL将目录树的位置通过make参数的形式传递给buildroot即可,待到整个项目结构搭建完成之后,我们就会看到这种集成方式。
buildroot/$ make BR2_EXTERNAL=/path/to/foo menuconfig
注意,执行maked的目录为buirdroot的根目录。而且,我们可以再次执行上述命令从而导入另一个项目的目录树位置。
br2-external tree必须包含下面几个文件:
external.desc
external.mk
Config.in
除去上面几个必须的文件之外,其他的文件或目录可以按照需要自行添加,后续会给出一个具体的br2-externnal tree布局示例。
下面详细介绍一下,上面三个主要文件的含义。
该文件主要用于说明br2-external tree的名字和基本描述。
该文件的基本格式是:每行表示一个具体描述项,每行的开头是一个关键字,后面跟一个冒号,然后是若干个空格,最后就是一个用于描述关键字的字符串。主要包括两个关键字:
name,必须定义的描述项,用于描述br2-external tree的名字。name的命名字符必须是[A-Za-z0-9_],其他的字符不允许出现。Buildroot会设置BR2_EXTERNAL_$(NAME)_PATH环境变量,用于指定br2-exteranl tree的绝对位置,所以,我们可以在自己的br2-external tree中使用该环境变量。同时,因为buildroot可以同时引用多个br2-extreanl tree,所以,这里在命名name时,应该防止与其他的br2-exteranal tree冲突。
desc,可选的,用一段简短的话,来描述一下该br2-exteranal tree。buildroot使用环境变量BR2_EXTERNAL_$(NAME)_DESC来指定该描述。
本项目的上述两个关键字定义如下:
name:LinuxVersatile
desc: Linux For QEMU ARM Versatile
注意:BR2_EXTERNAL_ ( N A M E ) P A T H 和 B R 2 _ E X T E R N A L _ (NAME)_PATH和BR2\_EXTERNAL\_ (NAME)PATH和BR2_EXTERNAL_(NAME)_DESC两个环境变量对于:Kconfig、Makefile、post-build、post-image和in-fakeroot scripts都是可见的,在上述文件中我们可以直接使用上面两个环境变量。
这两个文件主要用于定义如何配置和编译项目相关的组件,其与Buildroot中组件的管理方式类似。我们可以把我们自己的应用程序或者buildroot不支持的第三方库放到这里。如果项目没有特别的组件,那么这个文件为空。
顾名思义,Config.in用于管理组件的配置,externnal.mk用于管理makefile文件。Buildroot会将Config.in包含的所有组件的Config.in,显示到Buildroot的顶级配置菜单里,供用户选择配置,同时,Buildroot也会包含external.mk包含的所有组件的编译配置文件。
Config.in的一般形式如下:
source "$BR2_EXTERNAL_BAR_42_PATH/package/package1/Config.in"
source "$BR2_EXTERNAL_BAR_42_PATH/package/package2/Config.in"
每个组件中同样会包含自己的配置文件:Config.in。
external.mk文件的定义如下:
include $(sort $(wildcard $(BR2_EXTERNAL_BAR_42_PATH)/package/*/*.mk))
而后,在 B R 2 E X T E R N A L B A R 4 2 P A T H / p a c k a g e / p a c k a g e 1 和 BR2_EXTERNAL_BAR_42_PATH/package/package1和 BR2EXTERNALBAR42PATH/package/package1和BR2_EXTERNAL_BAR_42_PATH/package/package2中创建组件的副本,并定义相应的Config.in和.mk文件。
也可以在Config.in中定义特殊的配置选项,在external.mk中定义make相关的逻辑。
该目录用于保存Buildroot的defconfigs,这个可以定义基于特定项目的defconfig文件,Buildroot可以自动的识别这些文件,并可以通过make list-defconfig展示出来,所以,我们可以使用make _deconfig来加载defconfig。
本项目的配置文件名为qemu_linux_versatile_defconfig,里面保存了该项目相关的配置文件。主要的配置包括硬件平台的定义、交叉编译器的配置、根文件系统相关的配置以及Linux内核相关的配置。
下面为qemu_linux_versatile_defconfig的配置内容:
#Target options
BR2_arm=y
BR2_cortex_a9=y
BR2_ARM_INSTRUCTIONS_THUMB2=y
#Build optinons
BR2_DL_DIR="/home/lhl/develops/linuxCard/businesscard-linux/buildroot/dl"
BR2_JLEVEL=2
BR2_CCACHE=y
#Toolchain
BR2_TOOLCHAIN_BUILDROOT_USE_SSP=y
BR2_TOOLCHAIN_BUILDROOT_CXX=y
BR2_PACKAGE_HOST_GDB=y
#System Configuration
BR2_TARGET_GENERIC_HOSTNAME="qemu-arm"
BR2_TARGET_GENERIC_ISSUE="Welcome to Linux Versatile!"
BR2_TARGET_GENERIC_GETTY_PORT="ttyAMA0"
BR2_SYSTEM_DHCP="eth0"
BR2_ROOTFS_OVERLAY="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/rootfs_overlay"
BR2_ROOTFS_POST_BUILD_SCRIPT="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/post-build.sh"
BR2_ROOTFS_POST_IMAGE_SCRIPT="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/after-build.sh "
#Kernel
BR2_LINUX_KERNEL=y
BR2_LINUX_KERNEL_CUSTOM_GIT=y
BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git"
BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v5.5.3"
BR2_LINUX_KERNEL_USE_CUSTOM_CONFIG=y
BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/linux_defconfig"
BR2_LINUX_KERNEL_DTS_SUPPORT=y
BR2_LINUX_KERNEL_CUSTOM_DTS_PATH="/home/lhl/work/Linux/LinuxVersatile/board/LinuxVersatile/misc/linuxVersatile.dts"
#Target packages
BR2_PACKAGE_ASCII_INVADERS=y
BR2_PACKAGE_CHOCOLATE_DOOM=y
BR2_PACKAGE_SL=y
# Filesystem images
BR2_TARGET_ROOTFS_EXT2=y
BR2_TARGET_ROOTFS_EXT2_4=y
BR2_TARGET_ROOTFS_EXT2_LABEL="rootfs"
生成上述配置文件的方式,可以使用BUildroot的make savedefconfig命令,该命令的具体执行方式如下:
#进入buildroot主目录
$cd buildroot
#进行buildroot配置
$ make menuconfig
#另存buildroot配置文件
$make savedefconfig BR2_DEFCONFIG=/home/lhl/work/Linux/LinuxVersatile/configs/qemu_linux_versatile_defconfig
按照buildroot的br2-external tree的配置要求,下面是本项目的目录树节结构。
lhl@ubuntu18:~/work/Linux/LinuxVersatile$ tree
.
├── board
│ └── LinuxVersatile
│ ├── misc
│ │ ├── linux_defconfig
│ │ ├── linuxVersatile.dts
│ │ └── post-build.sh
│ └── rootfs_overlay
├── buildroot
|... ...
├── Config.in
├── configs
│ └── qemu_linux_versatile_defconfig
├── external.desc
├── external.mk
├── package
└── README.md
7 directories, 8 files
其中,buildroot目录为Buildroot的主目录,因为里面的文件太多,所以并未一一列出。
项目的配置文件完成之后,可以进行下一步的编译工作了,切换到buildroot的目录,执行如下命令:
#加载项目配置文件
$ make BR2_EXTERNAL=$PWD/../ qemu_linux_versatile_defconfig
#编译
$ make
qemu\_linux\_versatile_defconfig的配置项BR2_JLEVEL=2 用于标明make执行的并发CPU数量,这里配置的是2。相当于make -j 2参数,不过buildroot不支持该这种使用方式。
因为需要下载相关的组件,所以编译时十分的缓慢,请耐心等待。
编译完成之后,在buildroot的output为编译产生的文件,我们主要关注的是images中的文件。
lhl@ubuntu18:~/work/Linux/LinuxVersatile/buildroot/output/images$ tree
.
├── linuxVersatile.dtb
├── rootfs.ext2
├── rootfs.ext4 -> rootfs.ext2
├── rootfs.tar
├── vexpress-v2p-ca9.dtb
└── zImage
0 directories, 6 files
运行qemu-system-arm命令,测试内核和rootfs的正确性。
$ qemu-system-arm -M vexpress-a9 -smp 2 -m 1024M -kernel ./zImage -append "root=/dev/mmcblk0 rw console=ttyAMA0" -nographic -dtb ./linuxVersatile.dtb -sd ./rootfs.ext4
各个参数的含义如下:
- M:表示硬件平台,本项目为vexpress-a9
-smp:CPU核心数量,本项目为2个
-m:指定内存大小,本项目为1GBytes
-kernel:指定Linux内核文件
-append:指定Linux内核启动时的'cmdline',本项目中的root=/dev/mmcblk0 rw,指定rootfs文件系统保存在mmcblk0设备中,console=ttyAMA0表示内核的启动信息打印到ttyAMA0虚拟终端
-dtb:指定Linux内核相关的dts配置数据
-nograhpic:关闭图形界面信息输出,并将输出信息重定向到串口终端上,本项目就是上面的ttyAMA0虚拟串口
-sd:挂在sd文件,该文件保存了rootfs系统,内核识别的设备文件为mmcblk0
下面为内核启动后进入文件系统
Booting Linux on physical CPU 0x0
Linux version 5.5.3 (lhl@ubuntu18.04) (gcc version 8.3.0 (Buildroot 2020.02-git-01375-g25b1dc4613)) #2 SMP Wed Feb 19 21:24:36 CST 2020
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: V2P-CA9
Memory policy: Data cache writealloc
Reserved memory: created DMA memory pool at 0x4c000000, size 8 MiB
OF: reserved mem: initialized node vram@4c000000, compatible id shared-dma-pool
cma: Reserved 16 MiB at 0x9f000000
percpu: Embedded 19 pages/cpu s45644 r8192 d23988 u77824
Built 1 zonelists, mobility grouping on. Total pages: 260096
Kernel command line: root=/dev/mmcblk0 rw console=ttyAMA0
printk: log_buf_len individual max cpu contribution: 4096 bytes
printk: log_buf_len total cpu_extra contributions: 12288 bytes
printk: log_buf_len min size: 16384 bytes
printk: log_buf_len: 32768 bytes
printk: early log buf free: 14896(90%)
... ....
Welcome to Linux Versatile!
qemu-arm login: root
# uname -a
Linux qemu-arm 5.5.3 #2 SMP Wed Feb 19 21:24:36 CST 2020 armv7l GNU/Linux
# cat /etc/os-release
NAME=Buildroot
VERSION=2020.02-git-01375-g25b1dc4613
ID=buildroot
VERSION_ID=2020.02-git
PRETTY_NAME="Buildroot 2020.02-git"
#
我们,可以将上述命加到after-build.sh中,这样buildroot执行完之后,就会自动启动qemu,从而测试系统的正确性。
#!/bin/bash
qemu-system-arm -M vexpress-a9 -smp 2 -m 1024M -kernel $BASE_DIR/images/zImage -append "root=/dev/mmcblk0 rw console=ttyAMA0" -nographic -dtb $BASE_DIR/images/linuxVersatile.dtb -sd $BASE_DIR/images/rootfs.ext4
好了,以上就是使用Buildroot来管理Linux内核和根文件系统,然后通过qemu模拟ARM 平台进行相关测试的例子。尽情享受Linux带来的技术盛宴把!