如何基於Buildroot和Qemu搭建嵌入式Linux系統環境

爲了能夠更好的學習嵌入式Linux內核,本文基於qemu-system-arm工具模擬ARM公司的VersatileExpress硬件環境,基於buildroot工具製作rootfs,並且擴展了buildroot,增加內核配置管理,定製rootfs等功能。

我的上一篇文章,基於Buildroot的rootfs製作,講述瞭如何基於Buildroot製作一個rootfs,所以,本文對於該部分進行了省略,有需要的請移步到這篇文章。

準備工具

  1. 安裝qemu
Ubuntu安裝qemu比較簡單,一條命令搞定.

$ sudo apt install qemu libncurses5-dev build-essential
  1. 構建Git版本庫
$sudo apt install git

$mkdir ~/work/Linux/LinuxVersatile

$ git init

  1. 基於LinuxVersatile創建buildroot版本庫

由於buildroot也是基於Git管理的,所以我們使用git submodule完成對buildroot的管理.

$ git submodule add git://git.busybox.net/buildroot

更新buildroot

$ git submodule update --init

基於builroot定製項目

一般情況下,對於一個特定的項目,我們要做的可能包括如下幾方面:

  • 配置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-external tree模式

面一節介紹瞭如何構建特定項目的目錄樹,並且指定本項目使用br2-extern tree模式,下面介紹一下如何創建該目錄樹,並且說明一下如何實現相關的配置文件。需要思考的一個問題是,特定的項目
如何與buildroot進行集成呢?其實很簡單,我們只需通過 BR2_EXTERNAL將目錄樹的位置通過make參數的形式傳遞給buildroot即可,待到整個項目結構搭建完成之後,我們就會看到這種集成方式。

buildroot/$ make BR2_EXTERNAL=/path/to/foo menuconfig
注意,執行maked的目錄爲buirdroot的根目錄。而且,我們可以再次執行上述命令從而導入另一個項目的目錄樹位置。

br2-external tree佈局

br2-external tree必須包含下面幾個文件:

  • external.desc

  • external.mk

  • Config.in

除去上面幾個必須的文件之外,其他的文件或目錄可以按照需要自行添加,後續會給出一個具體的br2-externnal tree佈局示例。

下面詳細介紹一下,上面三個主要文件的含義。

external.desc

該文件主要用於說明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_(NAME)PATHBR2_EXTERNAL_(NAME)_PATH和BR2\_EXTERNAL\_(NAME)_DESC兩個環境變量對於:Kconfig、Makefile、post-build、post-image和in-fakeroot scripts都是可見的,在上述文件中我們可以直接使用上面兩個環境變量。

Config.in和external.mk

這兩個文件主要用於定義如何配置和編譯項目相關的組件,其與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))

而後,在BR2EXTERNALBAR42PATH/package/package1BR2_EXTERNAL_BAR_42_PATH/package/package1和BR2_EXTERNAL_BAR_42_PATH/package/package2中創建組件的副本,並定義相應的Config.in和.mk文件。

也可以在Config.in中定義特殊的配置選項,在external.mk中定義make相關的邏輯。

configs目錄

該目錄用於保存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 ([email protected]) (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帶來的技術盛宴把!

發佈了122 篇原創文章 · 獲贊 132 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章