Linux利器:QEMU!用它模拟开发板能替代真开发板?
不想錯(cuò)過(guò)我的推送,記得右上角-查看公眾號(hào)-設(shè)為星標(biāo),摘下星星送給我!
QEMU,搞嵌入式開(kāi)發(fā)的一定不陌生,最近各大群里都討論瘋了,說(shuō)它是Linux利器一點(diǎn)也不夸張。它是一款知名的而且開(kāi)源的模擬器(官網(wǎng):https://www.qemu.org/),它能在 X86 PC 上運(yùn)行(其實(shí)它也可以在你的 Arm 開(kāi)發(fā)板上運(yùn)行,我們今天先不討論這種場(chǎng)景),能夠模擬 Arm、MIPS、RISC-V 等各種 CPU 和開(kāi)發(fā)板,以及 網(wǎng)卡、聲卡、鍵盤、sdcard、emmc、usb等各種外設(shè)。
你可以把它當(dāng)作一塊召之即來(lái)的開(kāi)發(fā)板,在上面運(yùn)行 U-Boot、Linux Kernel、甚至 Ubuntu 等各種軟件和操作系統(tǒng)。
有時(shí)候我們想體驗(yàn)一下 mainline 上最新的 U-Boot 或者 Linux Kernel,可是卻發(fā)現(xiàn)手邊沒(méi)有合適的板子,或者手邊的板子搭載的 U-Boot 和 Linux Kernel 版本都比較低,這時(shí)候 QEMU 可以幫你迅速實(shí)現(xiàn)這一愿望。
我是Andy, Linux內(nèi)核開(kāi)發(fā)者。現(xiàn)從事Arm芯片上Linux 系統(tǒng)移植和優(yōu)化工作,并向 u-boot 和 linux kernel開(kāi)源社區(qū)貢獻(xiàn)了大量補(bǔ)丁。從 Cortex-M3 到 Arm64,RT-Thread 到 Linux kernel, hack 不止,其樂(lè)無(wú)窮。受達(dá)爾聞邀約為嵌入式開(kāi)發(fā)愛(ài)好者分享這篇好文,更多我的文章,可以關(guān)注我的公眾號(hào):HackforFun
接下來(lái)我?guī)ьI(lǐng)大家一起來(lái)感受一下QEMU的強(qiáng)大和樂(lè)趣:手把手教你在 Ubuntu 系統(tǒng)中通過(guò) QEMU 來(lái)運(yùn)行基于 Arm CPU 的Linux系統(tǒng)吧。
QEMU安裝
我們安裝的是 Arm 版本的 QEMU,如果直接在 Ubuntu 上用sudo apt install qemu-system-arm命令安裝的話,得到的 QEMU 版本比較舊,最好直接通過(guò)源碼去編譯。
我在 Ubuntu 18.04 系統(tǒng)上發(fā)現(xiàn)系統(tǒng)默認(rèn)安裝的 QEMU 在圖形模式(不帶 -nographic 參數(shù))下無(wú)法啟動(dòng)。
參考上面步驟編譯成功后,得到:qemu-system-arm和qemu-system-aarch64。
前者用來(lái)模擬 32 位的 Arm cpu,比如 Arm9 /Arm11、 Cortex-A7/A9/A15 。
后者用來(lái)模擬 64 位的 Arm cpu,比如 Arm Cortex A53,A57。
可以用qemu-system-arm -machine help命令來(lái)查看所支持的開(kāi)發(fā)板:
是不是有很多熟悉的開(kāi)發(fā)板都在里面,i.MX、EXYNOS 這些知名的芯片都有包含。
這里我們使用 vexpress-a9 這款開(kāi)發(fā)板。vexpress-a9 是 Arm 公司自己設(shè)計(jì)的一款 4 核 Cortex-A9 開(kāi)發(fā)板,U-Boot、Linux Kernel 和 QEMU 對(duì)這款開(kāi)發(fā)板都做了完整的支持。
編譯U-Boot
第一步:U-Boot 代碼下載:
git clone https://gitlab.denx.de/u-boot/u-boot.git下載完后,可以看到 configs 目錄下有針對(duì)這款開(kāi)發(fā)板的配置文件:
vexpress_ca9x4_defconfig第二步:編譯
make vexpress_ca9x4_defconfig make?CROSS_COMPILE=arm-linux-gnueabihf-?all最終編譯生成 elf 格式的可執(zhí)行文件 u-boot 和純二進(jìn)制文件u-boot.bin,其中 QEMU 可以啟動(dòng)的為 elf 格式的可執(zhí)行文件 u-boot。
編譯 Buildroot
啟動(dòng)一個(gè) Arm Linux 系統(tǒng),一般都要必須的三件套:Bootloader、Linux Kernel、rootfs(根文件系統(tǒng))。
在很久以前,制作 rootfs 是一件很麻煩的事情:交叉編譯 busybox,然后手動(dòng)建立標(biāo)準(zhǔn)的 Linux 系統(tǒng)目錄,再把編譯 busybox 生成的各種文件和庫(kù)拷貝過(guò)來(lái)。如果還需要其他的模塊,再交叉編譯。如果交叉編譯的某個(gè)模塊依賴其他的庫(kù),還想要辦法解決這個(gè)依賴關(guān)系。最后還要手動(dòng)建立設(shè)備節(jié)點(diǎn),設(shè)置對(duì)應(yīng)的權(quán)限。一步一步做下去,任意一個(gè)環(huán)節(jié)都不能出錯(cuò)。否則啟動(dòng)的時(shí)候不知道會(huì)遇到什么莫名其妙的問(wèn)題。
Buildroot 項(xiàng)目出現(xiàn)之后,如同它的 Slogan:MakingEmbedded Linux Easy,構(gòu)建 rootfs 就變得輕松了許多,用一個(gè)群友的話說(shuō):
“自從用了buildroot,我就告別了刀耕火種的野蠻生活。那種文件系統(tǒng)自己定制,需要任何工具都要自己下源碼交叉編譯然后被各種庫(kù)問(wèn)題搞的焦頭爛額的時(shí)代一去不復(fù)返了。”
◆?代碼下載:
git clone git://git.buildroot.net/buildrootBuildroot 代碼倉(cāng)庫(kù)默認(rèn)只包含一個(gè)編譯框架,所以代碼量很小,下載起來(lái)很快。真正構(gòu)建 rootfs需要的各種代碼包是根據(jù)你的配置選項(xiàng),在編譯的時(shí)候才開(kāi)始下載的。
◆?配置:
Buildroot 提供了和 U-Boot、Linux Kernel 等主流開(kāi)源項(xiàng)目一樣的 menucoinfig 配置接口,可以通過(guò)make help來(lái)查詢所支持的各種命令:
開(kāi)發(fā)者只要執(zhí)行make menuconfig命令,就能通過(guò)這個(gè)熟悉的界面去選擇自己需要的各種組件,定制自己的 rootfs:
首先要配置的是 Target options 選項(xiàng):
大部分 Arm 都是小端模式,所以選上 little endian 。
這款開(kāi)發(fā)板的 CPU 是 cortex-A9。我們將使用 Linaro GCC 進(jìn)行編譯,Linaro 的 GCC 默認(rèn)都打開(kāi)了 hardfloat 的支持,所以選上 VFP extension 和 EABIhf。
交叉編譯工具 Linaro GCC 的安裝可以參考前面的文章:一次搞定 Arm Linux 交叉編譯。
Build options選項(xiàng):
第一個(gè)選項(xiàng)是設(shè)置最后生成的配置文件的保存路徑,buildroot 可以針對(duì)不同的板子生成特定的 defconfig 文件,默認(rèn)保存在 configs 目錄下。
自己修改各項(xiàng)配置后,執(zhí)行make savedefconfig命令,就會(huì)生成新的 defconfig 文件:
下次編譯之前,可以直接執(zhí)行make ca9_mini_defconfig命令來(lái)加載已有的配置。
第二個(gè)選項(xiàng)設(shè)置 buildroot 下載的各種第三方包的存儲(chǔ)路徑,默認(rèn)在 dl 目錄下:
設(shè)置 Toolchain
因?yàn)檫@里使用電腦上自己安裝的 toolchain,所以我們這里選 External toolchain 和 Custom toolchain
然后在 Toolchain path 中填寫 toolchian 在電腦上安裝的位置,如果不知道具體位置,用which命令查看:
另外要注意 Toolchain prefix 這個(gè)前綴別寫錯(cuò)。
設(shè)置 toolchain 的版本和用來(lái)編譯這個(gè) toolchain 的內(nèi)核頭文件的內(nèi)核的版本:
Toolchain 的版本我們根據(jù) Toolchain 的名字或者通過(guò)arm-linux-gnueabihf-gcc ?-v 命令就可以查到。
編譯 Toolchain 的內(nèi)核頭文件對(duì)應(yīng)的內(nèi)核的版本是什么呢?
我們?cè)谶@個(gè)選項(xiàng)上敲 h 鍵,會(huì)看到下面的幫助選項(xiàng):
原來(lái)這個(gè)版本可以在 toolchain 里面的 version.h 這個(gè)文件查到:
打開(kāi)這個(gè)文件:
arm-linux-gnueabihf/libc/usr/include/linux/version.h263680 對(duì)應(yīng)的十六進(jìn)制為 0x40600,右移 16 位,得到的版本號(hào)為 4。這就是上面 4.0.x 的由來(lái)。
這里也教給了大家一個(gè)小竅門:當(dāng)我們?cè)?make menuconfig 做配置的時(shí)候,如果遇到了看不懂的選項(xiàng),直接在這個(gè)選項(xiàng)上敲 h 鍵,會(huì)看到一些有用的幫助信息,對(duì)這個(gè)選項(xiàng)做進(jìn)一步解釋。
因?yàn)?vexpress_a9 內(nèi)核啟動(dòng)的控制臺(tái)的名字叫做 ttyAMA0,所以我們還要在 :System configuration->Run a getty(login prompt)?after boot選項(xiàng)中配置 TTY Port 為 ttyAMA0。否則文件系統(tǒng)掛載后無(wú)法進(jìn)入控制臺(tái)。
我們把編譯的 rootfs 以 initramfs 的形式和 Linux Kernel 鏈接在一起,為了讓根文件系統(tǒng)鏡像盡量小,可以對(duì)文件系統(tǒng)采用 lz4 壓縮,所以 Filesystem images 還要做如下配置:
到這里一個(gè)最精簡(jiǎn)的 buildroot 已經(jīng)配置完成。
如果還需要其他的命令或者工具,可以在 Target Packages 下面開(kāi)啟:
如果你需要的某個(gè)模塊,buildroot 里面沒(méi)有,還可以自己添加:
比如加入上面這個(gè)補(bǔ)丁,就可以讓 buildroot 在編譯的時(shí)候自動(dòng)下載 https://github.com/rockchip-linux/io.git 并編譯。
退出執(zhí)行make命令開(kāi)始編譯。
Buildroot 編譯的過(guò)程中會(huì)自動(dòng)通過(guò)網(wǎng)絡(luò)下載需要的各種包,所以要保證網(wǎng)絡(luò)暢通。
編譯完成后輸入信息如下圖:
編譯Linux Kernel
1)代碼下載
Linux mainline 已經(jīng)有了對(duì) vexpress_a9 這塊板子的支持:
這里面的 ca 就是 Cortex-a 的簡(jiǎn)寫,所以 ca9 就是 Cortex-A9,ca15 就是 Cortex-A15.
2)配置
把前面 buildroot 編譯的 rootfs.cpio.lz4 拷貝到 linux kernel 根目錄下:
cp../buildroot/output/images/rootfs.cpio.lz4 . make?ARCH=arm vexpress_defconfig執(zhí)行make ARCH=arm menuconfig命令,我們修改一些基本的配置:
在 General setup->Initramfs source file處填寫 rootfs.cpio.lz4, 就是我們前面從 Buildroot 拷貝過(guò)來(lái)的 rootfs,這里面我們把它和內(nèi)核編譯在一起,當(dāng)然,rootfs也可以單獨(dú)作為一個(gè)文件,放在獨(dú)立的分區(qū)去加載,這種方式我們可以留在以后去嘗試。
?
在 Kernel hacking->printk and dmesg options選項(xiàng)中選中第一項(xiàng),這樣打印的內(nèi)核 log 前面會(huì)附帶有時(shí)間戳信息,比較好看。
退出,保存,然后編譯:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8編譯完成。
如果在編譯 Linux kernel 或者 u-boot 的過(guò)程中,遇到什么錯(cuò)誤,可以參考我之前的文章:LinuxKernel 和 U-Boot 編譯的那些事。
啟動(dòng)QEMU
前面說(shuō)了,QEMU 可以模擬 sd 卡等外設(shè)。我們就把編譯好的固件放在一個(gè)模塊的 sdcard 上,讓 QEMU 從這張模擬的 sd 卡上啟動(dòng) Linux 系統(tǒng):
◆ 制作 sd 卡鏡像,并將它格式化成 fat 格式:
dd if=/dev/zero of=sd.img bs=4096 count=4096 mkfs.vfat sd.img◆ 把編譯好的 kernel zImage 和 dtb 文件拷貝到 sd.img 中
sudo?mount?sd.img?/mnt/?-o?loop,rw sudo?cp?arch/arm/boot/zImage?/mnt/ sudo?cp?arch/arm/boot/dts/vexpress-v2p-ca9.dtb?/mnt/ sudo?umount?/mnt◆ 啟動(dòng) QEMU
sudo qemu-system-arm -M vexpress-a9 -m 512M -kernel ../uboot-imx/u-boot-nographic -sd sd.img-M 參數(shù)指定啟動(dòng)的板子為 vexpress-a9。
-m 指定這塊板子的內(nèi)存為 512MB。
-kernel 指定 QEMU 啟動(dòng)時(shí)首先執(zhí)行的程序,我們這里指定為前面編譯好的 u-boot 可以執(zhí)行文件。
-sd 參數(shù)指定前面制作的 sd.img。
因?yàn)槲覀冞@里是從命令行啟動(dòng),所以加了 nographic 參數(shù)。
可以看到 u-boot 已經(jīng)順利啟動(dòng)并進(jìn)入命令,下面我們來(lái)啟動(dòng) Linux Kernel。
◆ 首先通過(guò) fatload 命令把 sd.img 里面的 zImage 和 dtb 文件讀到開(kāi)發(fā)板的內(nèi)存中:
fatload?mmc?0:0 0x62008000?zImage fatload?mmc?0:0?0x64008000?vexpress-v2p-ca9.dtb這里面的 0x62008000 和 0x64008000 分別對(duì)應(yīng) zImage 和 dtb 文件在內(nèi)存中的加載地址,這兩個(gè)地址是怎么來(lái)的呢:
在 Linux Kernel 中,有如下定義:?
這個(gè) textofs 定義的就是 Linux kernel zImage 執(zhí)行地址對(duì)應(yīng)的內(nèi)存偏移地址,默認(rèn)偏移為 0x8000。
在 u-boot 命令行中輸入bdinfo命令,可以查到這塊開(kāi)發(fā)板內(nèi)存的起始地址:
可以看到這塊開(kāi)發(fā)板的內(nèi)存其實(shí)地址為 0x60000000,所以對(duì)應(yīng)內(nèi)核的起始地址為:0x62008000
dtb 的加載地址沒(méi)有特別的要求,一般注意和 Linux Kernel Image 避開(kāi),不要重疊即可。
◆ 通過(guò) bootz 命令啟動(dòng) Linux Kernel:
bootz 0x62008000 - 0x64008000?
啟動(dòng)到最后輸入root就可以進(jìn)入命令行控制臺(tái)。
到這里,我們就順利的在 QEMU 上把 Arm linux 運(yùn)行起來(lái)了。
QEMU 能否完全替代開(kāi)發(fā)板?
QEMU 是一個(gè)模擬器,它和真正的開(kāi)發(fā)板還是有一定的區(qū)別:
它無(wú)法完全模擬真實(shí)的硬件行為,也很難模擬一個(gè) gpio 讓你去拉高拉低或去點(diǎn)一個(gè) led 燈,又或者去模擬 dram,一個(gè) LCD 接口,讓你去接一個(gè)顯示屏….看起來(lái)它不能模擬的東西太多了,那它到底有什么用呢?
它的強(qiáng)項(xiàng)是模擬實(shí)現(xiàn)那些不涉及具體硬件外設(shè)的場(chǎng)景,比如:
你想快速體驗(yàn)一下最新的 u-boot 和 linux kernel,它拿過(guò)來(lái)就能跑。
你想在 Arm 上運(yùn)行 Ubuntu、Debian 這些時(shí)髦的 Linux 發(fā)行版,用它就行。
你想研究u-boot 或者 linux kernel 的啟動(dòng)流程,它也很合適。我有時(shí)候在具體的開(kāi)發(fā)板上移植 Linux 內(nèi)核的時(shí)候,發(fā)現(xiàn)某個(gè)流程跑的很異常,我又不確定正常的流程是什么樣的時(shí)候,我就直接拿 QEMU 來(lái)跑一下做對(duì)比。
你想了解文件系統(tǒng)的掛載過(guò)程,它很合適。
你想學(xué)習(xí) Arm 匯編,完全可以在 QEMU 上跑,配合 GDB 就能單步調(diào)試?yán)病?/p>
你想學(xué)習(xí)?Linux 設(shè)備樹(shù),它可以拿來(lái)做實(shí)驗(yàn)….
再或者你想編譯某個(gè)開(kāi)源的項(xiàng)目在 Arm 開(kāi)發(fā)板上運(yùn)行,而交叉編譯比較困難,就可以考慮直接在 QEMU 上運(yùn)行一個(gè) Arm Ubuntu 來(lái)編譯,也許會(huì)簡(jiǎn)單很多。
假設(shè)你以前一直玩單片機(jī)、做硬件,也想體驗(yàn)下 Arm Linux 開(kāi)發(fā),用 QEMU 試試再適合不過(guò)了。用在對(duì)的場(chǎng)景,QEMU 就是一款召之即來(lái)的利器!
END
? 推薦閱讀:
? ??專輯|Linux文章匯總
? ??專輯|程序人生
? ??專輯|C語(yǔ)言
嵌入式Linux
微信掃描二維碼,關(guān)注我的公眾號(hào)?
總結(jié)
以上是生活随笔為你收集整理的Linux利器:QEMU!用它模拟开发板能替代真开发板?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Oracle和MySQL语法区别
- 下一篇: Mysql 常用命令