@[toc]
qemu環(huán)境準備
軟件準備
- 日期:2018.04.12
- 宿主機系統(tǒng)平臺:Win7 64 + VMware(10~12)+ Ubuntu16.04 + VMwareTools
- 代碼編輯管理工具:git、vim伙窃、vscode
- u-boot 版本:u-boot-2015-04
- Linux kernel版本:linux-4.x
- busybox版本:1_24_stable
- 交叉編譯工具鏈:arm-none-linux-gnueabi-
- qemu版本:stable-2.8
安裝QEMU編譯依賴包
sudo apt-get install zlib1g-dev
sudo apt-get install libglib2.0-0 libglib2.0-dev
sudo apt-get install libsdl1.2-dev
sudo apt-get install libpixman-1-dev libfdt-dev
sudo apt-get install gcc-arm-linux-gnueabi
自動安裝qemu
sudo apt-get install qemu
手動編譯安裝(推薦)
為什么推薦手動安裝:因為我們操作虛擬硬件時需要源碼包提供虛擬硬件信息
- 獲取QEMU源代碼:
git clone https://gitee.com/mirrors/qemu
(碼云上找最新official) - 切換到一個穩(wěn)定版本:
git checkout v2.8.0
(git branch -a
查看所有分支) - 安裝配置環(huán)境:
git submodule update --init dtc
- 編譯配置:
./configure --prefix=/opt/qemu --target-list=arm-softmmu,arm-linux-user --enable-debug
- 編譯安裝:
make && make install
課外知識:
查看版本標簽:git tag(標簽可以查看更細分支)
基于遠程分支新建一個本地分支:git checkout remotes/origin/stable-2.8 -b 2.8
QEMU裸跑hello程序
這里的裸跑C hello程序并不是真的裸跑呀潭,而是直接使用qemu已經(jīng)建立好的完整ARM嵌入式系統(tǒng)來跑一個通用的helloworld程序备籽,其他架構類似冀续。
新建代碼:編寫一個通用的helloworld程序源代碼。
靜態(tài)編譯:arm-none-linux-gnueabi-gcc hello.c -static
執(zhí)行程序:qemu-arm a.out
新建代碼:編寫一個通用的helloworld程序源代碼熔萧。
靜態(tài)編譯:arm-linux-gnueabi-gcc hello.c -static
執(zhí)行程序:qemu-arm a.out
- hello.c源代碼:
#include <stdio.h>
int main(void)
{
printf("hello world!\n");
return 0;
}
制作Linux內核及設備樹文件
- 官網(wǎng)內核版本選擇:longterm(長期維護)4.x(其中X為偶數(shù)為穩(wěn)定)
- 解壓內核:
tar -xJvf linux-4.4.tar.xz ; cd linux-<tab>
- 修改Makefile:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
- 配置內核:
cp arch/arm/configs/vexpress_defconfig ./.config ; make menuconfig
(arch/arm/configs目錄有各種默認配置) - 編譯內核:
make zImage
[V=1表示完整顯示編譯過程]
(arch/arm/boot/zImage糖驴、dts/(不同開發(fā)板有不同dts文件)) - 編譯內核模塊:
make modules
[V=1表示完整顯示編譯過程] - 編譯dtb文件:
make dtbs
[V=1表示完整顯示編譯過程]
使用QEMU啟動Linux內核
文件列表:zImage(arch/arm/boot/)僚祷、xxx.dtb(arch/arm/boot/dts)
根據(jù)上述提供的測試文件,執(zhí)行如下命令來啟動Linux內核:
qemu-system-arm -M vexpress-a9 -m 512M -kernel ./zImage -dtb ./vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
(成功啟動內核如下贮缕,由于沒rootfs辙谜,下面情況正常)查看qemu-system-arm進程號,并根據(jù)進程號kill它:
pkill process_name
最簡單的根文件系統(tǒng)(initramfs)
文件準備:init.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
int i = 0;
while(1)
{
printf("%d: This is a simplest rootfs\n", i++);
sleep(2);
}
return 0;
}
制作initramfs
- 靜態(tài)編譯init.c:
arm-none-linux-gnueabi-gcc init.c -o init -static
利用init程序制作initramfs:find ./ -name "init" | cpio -o -H newc > initramfs
或者等效于find ./ -name "init" | cpio --create --format=newc > initramfs
QEMU啟動測試
qemu-system-arm -initrd initramfs \
-M vexpress-a9 -m 512M \
-kernel zImage \
-dtb vexpress-v2p-ca9.dtb \
-nographic \
-append "init=/init console=ttyAMA0"
啟動效果:啟動成功后會循環(huán)打印
制作busybox根文件系統(tǒng)
創(chuàng)建目錄樹
mkdir rootfs
ls -F / | grep "/" | xargs -d '\n' -i mkdir -p rootfs/{}
mkdir rootfs/lib/modules
創(chuàng)建基本設備文件
- 創(chuàng)建基本設備文件:(null,zero,console,tty1~4)
mknod -m 666 /dev/null c 1 3
(設備號參考宿主機相應設備號)
獲取跷睦、編譯和安裝busybox
- 下載源代碼:http://www.busybox.net/downloads (
wget https://busybox.net/downloads/busybox-1.27.2.tar.bz2
) - 修改Makefile:
ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
- busybox配置:
make menuconfig
Busybox Settings --->
General Configuration --->
[*] Don't use /usr
Build Options --->
[*] Build Busybox as a static binary(no shared libs)
(arm-none-linux-gnueabi-)Cross Compiler Prefix
Installation Options --->
(/home/workdir/QEMU-Embedded/rootfs)Busybox installation Prefix
編譯筷弦、安裝:make [V=1表示完整顯示編譯過程]; make install
自動化腳本:setup.sh
#!/bin/bash
KERNEL_VERSION=linux-4.6.6
BUSYBOX_VERSION=busybox-1.27.2
DIR_CUR=`pwd`
DIR_ROOTFS=${DIR_CUR}/rootfs
DIR_BUSYBOX=${DIR_CUR}/${BUSYBOX_VERSION}
DIR_KERNEL=${DIR_CUR}/${KERNEL_VERSION}
function build_dirtree()
{
mkdir ${DIR_ROOTFS}
ls -F / | grep "/" | xargs -d '\n' -i mkdir -p ${DIR_ROOTFS}/{}
mkdir -p ${DIR_ROOTFS}/lib/modules
mkdir -p ${DIR_ROOTFS}/etc/init.d
echo 'echo -------------------------------' > ${DIR_ROOTFS}/etc/init.d/rcS
echo 'echo QEMU ARM Linux' >> ${DIR_ROOTFS}/etc/init.d/rcS
echo 'echo -------------------------------' >> ${DIR_ROOTFS}/etc/init.d/rcS
chmod 755 ${DIR_ROOTFS}/etc/init.d/rcS
sudo mknod -m 666 ${DIR_ROOTFS}/dev/null c 1 3
sudo mknod -m 666 ${DIR_ROOTFS}/dev/console c 5 1
sudo mknod -m 666 ${DIR_ROOTFS}/dev/tty1 c 4 1
sudo mknod -m 666 ${DIR_ROOTFS}/dev/tty2 c 4 2
sudo mknod -m 666 ${DIR_ROOTFS}/dev/tty3 c 4 3
sudo mknod -m 666 ${DIR_ROOTFS}/dev/tty4 c 4 4
}
function compile_busybox()
{
# load busybox
#source ${HOME}/.profile
#tar -xjvf ${DIR_CUR}/${BUSYBOX_VERSION}.tar.bz2
echo "modify ARCH,CROSS_COMPILE,INSTALL_MOD_PATH,etc. of Makefile"
echo "Press Enter to continue"
read temp
vim ${DIR_BUSYBOX}/Makefile
make -C ${DIR_BUSYBOX} menuconfig #ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
make -C ${DIR_BUSYBOX} -j4 #ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
make -C ${DIR_BUSYBOX} install #INSTALL_MOD_PATH=./${DIR_ROOTFS}
}
function compile_kernel()
{
#tar -xJvf ${DIR_CUR}/${KERNEL_VERSION}.tar.xz
echo "modify ARCH,CROSS_COMPILE,INSTALL_MOD_PATH,etc. of Makefile"
echo "Press Enter to continue"
read temp
vim ${DIR_KERNEL}/Makefile
cp -rf ${DIR_KERNEL}/arch/arm/configs/vexpress_defconfig ${DIR_KERNEL}/.config
make -C ${DIR_KERNEL} menuconfig
make -C ${DIR_KERNEL} zImage -j4 V=1
make -C ${DIR_KERNEL} modules -j4 V=1
make -C ${DIR_KERNEL} dtbs -j4 V=1
}
function complete_dirtree()
{
cp -rf ${DIR_BUSYBOX}/_install/* ${DIR_ROOTFS}
cp -rf ${DIR_KERNEL}/arch/arm/boot/zImage ${DIR_CUR}
cp -rf ${DIR_KERNEL}/arch/arm/boot/dts/vexpress-v2p-ca9.dtb ${DIR_CUR}
cp -rf ${DIR_CUR}/a.out ${DIR_ROOTFS}
}
function build_image()
{
# build image
dd if=/dev/zero of=rootfs.ext3 bs=1M count=8
sudo mkfs.ext3 rootfs.ext3
sudo mkdir -p /mnt/sd
sudo mount -t ext3 -o loop rootfs.ext3 /mnt/sd
sudo cp -rf ${DIR_ROOTFS}/* /mnt/sd
sudo umount /mnt/sd
}
function run_qemu_nographic()
{
# run qemu
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel zImage \
-dtb ./vexpress-v2p-ca9.dtb \
-nographic \
-append "root=/dev/mmcblk0 rw init=/linuxrc console=ttyAMA0" \
-sd rootfs.ext3
}
function run_qemu_graphic()
{
# run qemu for GUI
qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel zImage \
-dtb ./vexpress-v2p-ca9.dtb \
-append "root=/dev/mmcblk0 rw console=tty0" \
-sd rootfs.ext3
}
function build_clean()
{
rm -rf $@
}
function main()
{
build_dirtree
compile_busybox
compile_kernel
complete_dirtree
build_image
#run_qemu_nographic
run_qemu_graphic
#build_clean ${DIR_ROOTFS}
#build_clean ${DIR_ROOTFS} ${DIR_BUSYBOX} ${DIR_KERNEL}
}
if [[ $# -ne 0 ]]; then
exit 0
else
main
exit 0
fi
自動化腳本流程梳理
- ① 創(chuàng)建rootfs目錄結構和基礎設備文件
- ② 配置編譯busybox
- ③ 配置編譯Linux內核
- ④ 把上面生成的busybox和kernel最終文件拷貝到rootfs
- ⑤ 把rootfs制作成sd鏡像文件
- ⑥ 使用該sd鏡像文件啟動qemu-system-arm
備注:上面整個shell腳本就是依據(jù)上面的流程來編碼的。
使用u-boot引導啟動Linux內核
u-boot的制作
- 獲取u-boot:
wget http://ftp.denx.de/pub/u-boot/u-boot-2014.10.tar.bz2
- 修改Makefile:
ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
- 修改u-boot相關啟動參數(shù)(源碼級)
- 配置編譯u-boot:
cp u-boot/configs/vexpress_ca9x4_defconfig u-boot/.config
make menuconfig
- 編譯u-boot:
make -j8
Linux內核uImage的制作
需要指定uImage的加載地址(編譯時指定):make LOADADDR=0x60003000 uImage -j4
QEMU啟動測試
qemu-system-arm -M vexpress-a9 -m 512M \
-kernel u-boot \
-nographic \
-net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \
-sd rootfs.ext3
掛載NFS文件系統(tǒng)
編輯文件:vim u-boot/include/configs/vexpress_common.h
CONFIG_BOOTCOMMAND的值改為:
tftp 0x60003000 uImage ; tftp 0x60500000 vexpress-v2p-ca9.dtb ; \
setenv bootargs 'root=/dev/nfs rw \
nfsroot=192.168.244.123:/home/rootfs init=/linuxrc \
ip=192.168.244.126 console=ttyAMA0' ; \
bootm 0x60003000 - 0x60500000 ; "
- 配置內核支持NFS文件系統(tǒng)
cp linux-4.13.0/arch/arm/configs/vexpress_defconfig linux-4.13.0/.config
make menuconfig ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
File System -->
[*] Network File System -->
<*> NFS client support
<*> NFS client support for NFS version 3
[*] NFS client support for the NFSv3 ACL
[*] Root file system on NFS
重新編譯內核:
make uImage ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-
qemu啟動測試:
qemu-system-arm -M vexpress-a9 -m 512M \
-kernel u-boot \
-nographic \
-net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \
-sd rootfs.ext3
QEMU裸機串口編程(arm926)
獲取板子支持信息
查看板子列表:
qemu-system-arm -machine help
通過qemu源代碼查詢相關硬件信息:
grep -Hn "cpu_model" qemu/hw/arm/versatilepb.c
通過qemu源代碼開發(fā)板的串口0寄存器硬件信息:
grep -Hn "UART" qemu/hw/versatilepb.c
查看交叉工具鏈支持的平臺:
grep -Hn "arm926" cross-tools/share/doc/arm-arm-none-linux-gnueabi/info/gcc.info
源文件列表:init.c抑诸、linker.lds烂琴、start.S、setup.sh
init.c
#define UART0_ARM926 ((*(volatile unsigned char *)0x101f1000))
#define UART0_A9 ((*(volatile unsigned char *)0x10009000))
void display(const char *string)
{
while(*string != '\0') {
UART0_ARM926 = *string; string++;
}
}
int my_init(void)
{
display("qemu-system-arm: Hello Open World\n");
while(1);
return 0;
}
linker.lds
ENTRY(_start)
SECTIONS {
. = 0x0;
.text : { start.o(.text)}
.data : {*(.data)}
.bss : {*(.bss)}
. = . + 0x500;
sp_top = .;
}
start.S
.global _start
_start:
ldr sp, =sp_top
bl my_init
b .
setup.sh
arm-none-linux-gnueabi-gcc -c -mcpu=arm926ej-s init.c -nostdlib
arm-none-linux-gnueabi-gcc hello.c -static
arm-none-linux-gnueabi-as start.s -o start.o
arm-none-linux-gnueabi-ld -T linker.lds init.o start.o -o start.elf
arm-none-linux-gnueabi-objcopy -O binary start.elf start.bin
qemu-arm a.out
#qemu-system-arm -M vexpress-a9 -nographic -kernel start.elf
qemu-system-arm -M versatilepb -nographic -kernel start.elf
rm -rf init.o start.o start.elf start.bin a.out
QEMU裸機串口編程(cortex-a9)
獲取板子支持信息
查看板子列表:qemu-system-arm -machine help
通過qemu源代碼查詢相關硬件信息:
grep -Hn "cpu_model" qemu/hw/arm/vexpress.c
通過qemu源代碼開發(fā)板的串口0寄存器硬件信息:
grep -Hn "UART" qemu/hw/vexpress.c
查看交叉工具鏈支持的平臺:
grep -Hn "a9" cross-tools/share/doc/arm-arm-none-linux-gnueabi/info/gcc.info
源文件列表:
init.c
#define UART0_ARM926 ((*(volatile unsigned char *)0x101f1000))
#define UART0_A9 ((*(volatile unsigned char *)0x10009000))
void display(const char *string)
{
while(*string != '\0') {
UART0_A9 = *string; string++;
}
}
int my_init(void)
{
display("qemu-system-arm: Hello Open World\n");
while(1);
return 0;
}
start.S
.global _start
_start:
ldr sp, =sp_top
bl my_init
b .
linker.lds
ENTRY(_start)
SECTIONS {
. = 0x60000000;
.text : { start.o(.text)}
.data : {*(.data)}
.bss : {*(.bss)}
. = . + 0x500;
sp_top = .;
}
setup.sh
arm-none-linux-gnueabi-gcc -c -mcpu=arm926ej-s init.c -nostdlib
arm-none-linux-gnueabi-gcc hello.c -static
arm-none-linux-gnueabi-as start.s -o start.o
arm-none-linux-gnueabi-ld -T linker.lds init.o start.o -o start.elf
arm-none-linux-gnueabi-objcopy -O binary start.elf start.bin
qemu-arm a.out
qemu-system-arm -M vexpress-a9 -nographic -kernel start.elf
#qemu-system-arm -M versatilepb -nographic -kernel start.elf
rm -rf init.o start.o start.elf start.bin a.out