2017年5月12日 星期五

作業系統之前的程式 for qemu arm/vexpress-a9 (0) - hello world

天地無極, 乾坤借法
我忽然興起在 qemu arm 中寫一個 bare metal hello world 的念頭, 這不是什麼新鮮事, 《一步步写嵌入式操作系统:ARM编程的方法与实践》裡頭用的就是這個方法, 只是作者用的模擬器不是 qemu, 而是 skyeye, 但我想用 qemu, 找到了《Hello world for bare metal ARM using QEMU 》, 不過這篇文章介紹的是 VersatilePB platform, 是 ARM926EJ-S core, 你一定和我有同樣的想法, 太舊了, 我想找 coretex a v7 系列的版本, 不過先來懷舊一下。

我已經有了樹莓派 2, 為什麼還需要用模擬器呢?

為了除錯, 我沒有可以在了樹莓派上用的 ice/jtag, 對於開發 bare metal 程式實在太困難, 總不能老是靠「冥想」, 有了 qemu arm 搭配 gdb 的除錯功能, 會有事半功倍的效果。

source code:
https://github.com/descent/bare-metal_qemu_arm_v7a

VersatilePB version
startup.s test.c test.ld

節錄 Hello world for bare metal ARM using QEMU
he QEMU emulator is written especially to emulate Linux guest systems; for this reason its startup procedure is implemented specifically: the -kernel option loads a binary file (usually a Linux kernel) inside the system memory starting at address 0x00010000. The emulator starts the execution at address 0x00000000, where few instructions (already in place) are used to jump at the beginning of the kernel image. The interrupt table of ARM cores, usually placed at address 0x00000000, is not present, and the peripheral interrupts are disabled at startup, as needed to boot a Linux kernel. Knowing this, to implement a working emulation I need to considerate a few things:

  • The software must be compiled and linked to be placed at 0x00010000
  • I need to create a binary image of our program
  • I can ignore interrupt handling for now

qemu load -kernel 後的檔案到 0x00010000, 但卻是從 0x00000000 執行, 有點不太理解。

而 qemu/arm 模擬的開發板 source code 在:

qemu/hw/arm
vexpress.c
versatilepb.c

可以看到其對 uart register 的定義。

hello world 程式只有 2 個檔案, 很容易理解, 最主要是設定 stack, call c function, 寫 uart register 完成字元的輸出。

make ARM926EJ_S=1
arm-none-eabi-as -g startup.s -o startup.o
arm-none-eabi-gcc -DARM926EJ_S -mcpu=arm926ej-s -c -g test.c -o test.o
arm-none-eabi-ld -g -T test.ld test.o startup.o -o test.elf
arm-none-eabi-objcopy -O binary test.elf test.bin

list 1
1 descent@debian64:bare-metal_qemu_arm_v7a$ qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin
2 pulseaudio: set_sink_input_volume() failed
3 pulseaudio: Reason: Invalid argument
4 pulseaudio: set_sink_input_mute() failed
5 pulseaudio: Reason: Invalid argument
6 Hello world!
7 hello ARM926EJ-S

ctrl+a x 可以離開 qemu。

list 1. L6 秀出了 hello ARM926EJ-S, 有沒很感動。

再來是 VEXPRESS-A9 的版本, 我以為如法泡製, 將 uart base address 改成 0x10009000 即可, 事實上, 我吃了更多的苦頭才找到答案。

VEXPRESS-A9 version
startup_vexpress_a9.o test.c vexpress_a9.ld

首先 qemu 指令要改成 -bios
qemu-system-arm -M vexpress-a9 -m 128M -bios test.a9.bin -nographic

好不容易看到 qemu 有畫面了, 但是是錯誤的訊息, 我一開始以為是 stack 沒設好的關係 (事實上也是啦), 透過反組譯後, 發現原本的 0x10000 記憶體 (我拿這段位址當 stack 區域) 無法寫入, 在參考 http://infocenter.arm.com/help/topic/com.arm.doc.dui0448i/DUI0448I_v2p_ca9_trm.pdf p3-3, 0x6000_0000-0x7FFF_FFFF 512MB Local DDR2 lower, use the ddr2 address.

使用 0x70000000 這個位址來設定 stack 就對了, 然後把 qemu 記憶體大小改成 512M。

qemu-system-arm -M vexpress-a9 -m 512M -bios test.a9.bin -nographic

list 1
1 descent@debian64:bare-metal_qemu_arm_v7a$ qemu-system-arm -M vexpress-a9 -m 512M  -bios test.a9.bin --nographic
2 pulseaudio: set_sink_input_volume() failed
3 pulseaudio: Reason: Invalid argument
4 pulseaudio: set_sink_input_mute() failed
5 pulseaudio: Reason: Invalid argument
6 Hello world!
7 hello VEXPRESS_A9

終於一舉功成。

另外 linker script 起始位置改為 0x0, 這樣才可以配合 gdb 來 single step。
在 gdb 下

x/8xb 0

可以看到載入的 test.a9.bin (linker script 起始位置為 0x10000 也一樣在 0x0 這裡看得到, 其實把 linker script 起始位置改為 0x0 也可以的), 就在這裡。

以下連結的程式碼也可以參考, 有 gic 的設定。該作者用 svn, 可用以下 svn 指令抓取 soure code。
http://ellcc.org/blog/?p=10
Updating From Outside Sources
svn co http://ellcc.org/svn/ellcc/trunk/baremetal
svn ls http://ellcc.org/svn/ellcc/trunk/

能成功設定 gic + timer 才是我的終極目的。若你比我先完成了, 請通知我讓我抄一下, 這樣我就不用那麼累了。

最後還是要提醒一下, 模擬器畢竟是模擬器, 和真實機器還是有點差異, 例如 uart 的設定不會這麼簡單, 通常還有 Baud 要設定。為了不讓自己白努力一場, 請在真實機器驗證整個模擬環境的程式碼, 正常來說, 應該還要修修改改。

最後的最後再提醒第二下, 這個程式碼看起來好像能使用 c 語言了, 好像可以正常呼叫 c function, 但其實離真正可用的 c 環境還有點距離, 若隨意使用 c 的各種宣告 (例如沒有初始值的全域變數), 是有可能出問題的。

因為 bss 沒設定, data section 也沒處理好, 很容易就踩雷了。我另外寫了一份完整版的 c runtime, 請參考 source code makefile 得知 v_a9.elf 相關的程式檔案, 這個版本打造了完整的 c runtime, bss/data section 均做了相關的初始化 。

本來我打算整合到 simple c++ library 當中的, 但我的目的僅僅是 gic+timer, 就不分心在其他事情上了。

若要練習的朋友, 請從 v_a9.elf 開始。

目前的版本無法在 vexpress-a15 qemu 上執行, 若你成功改到 vexpress-a15 qemu 上執行, 可以通知我一下嗎? 這可是幫了我很大的忙。

ref:

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。