qemu嵌入式ARM快速體驗

@[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.0git 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
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蜕乡,一起剝皮案震驚了整個濱河市奸绷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌层玲,老刑警劉巖号醉,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辛块,居然都是意外死亡畔派,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門润绵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來线椰,“玉大人,你說我怎么就攤上這事尘盼『┯洌” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵卿捎,是天一觀的道長配紫。 經(jīng)常有香客問我,道長午阵,這世上最難降的妖魔是什么躺孝? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮趟庄,結果婚禮上括细,老公的妹妹穿的比我還像新娘。我一直安慰自己戚啥,他們只是感情好奋单,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猫十,像睡著了一般览濒。 火紅的嫁衣襯著肌膚如雪呆盖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天贷笛,我揣著相機與錄音应又,去河邊找鬼。 笑死乏苦,一個胖子當著我的面吹牛株扛,可吹牛的內容都是我干的。 我是一名探鬼主播汇荐,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼洞就,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掀淘?” 一聲冷哼從身側響起旬蟋,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎革娄,沒想到半個月后倾贰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡拦惋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年匆浙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厕妖。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡吞彤,死狀恐怖,靈堂內的尸體忽然破棺而出叹放,到底是詐尸還是另有隱情,我是刑警寧澤挠羔,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布井仰,位于F島的核電站,受9級特大地震影響破加,放射性物質發(fā)生泄漏俱恶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一范舀、第九天 我趴在偏房一處隱蔽的房頂上張望合是。 院中可真熱鬧,春花似錦锭环、人聲如沸聪全。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽难礼。三九已至娃圆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蛾茉,已是汗流浹背讼呢。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谦炬,地道東北人悦屏。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像键思,于是被迫代替她去往敵國和親础爬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348