Zynq-Qemu上で動作するARMコードを書いてみた

ZynqLinuxとかToppers/FMPとかに頼らずに、ZynqQemu上でシリアル通信に文字を出力するプログラムを書いてみた。
要は、プログラム開始後すぐにシリアルデバイスを初期化して出力するだけのプログラム。

以下、プログラムの主要部分。
xuartps_initとかxuartps_putcharとかシリアルのデバドラ部分(?)はToppers/FMPのtarget/zynq_gcc/target_serial.cからお借り(コピペ)しました(・・・FMPに頼ってる!?笑)。
最初のインラインアセンブラは必要?

[hello_serial.c]

int main(void)
{
    asm("msr cpsr_cxsf, #(0x13 | 0x40 | 0x80)");
    xuartps_init();
    xuartps_putchar('C');
    while(1){}
}

そして、コンパイルからの実行。fmpだと、gccで出力されたELFファイルをqemuの-kernelオプションで読み込ませれば動いたような気がするんだけど、今回はべたのバイナリに変換してからじゃないと動作しなかった。
ビルドオプションはFMPのものを参考にしました。-Nの効能は不明。

>arm-none-eabi-gcc -c -mlittle-endian -mcpu=cortex-a9 -g -Wall -O2 
-DBASE_ADDR=0x0 -DCA9_PRIVATE_TIMER -D__TARGET_ARCH_ARM=7 -nostdlib 
-N hello_serial.c
>arm-none-eabi-objcopy -O binary hello_serial.o hello_serial.bin
>qemu-system-arm -M xilinx-zynq-a9 -m 1024 -smp 2 -serial null -serial stdio 
-kernel hello_serial.bin -nographic -monitor telnet::4444,server,nowait

QEMUモニターの機能メモその1

◎メモリダンプ(命令列変換!)
xp/[ダンプ数][形式][サイズ] メモリアドレス
xp:コマンド名
ダンプ数:10とか適当に
形式:x=16進,o,d,u,c, "i=アセンブラ命令"
サイズ:b(8bit),h(16bit),w(32bit),g(64bit)

例:xp/40xb 0x10000
00010000: 0xd3 0xf0 0x2f 0xe3 0x03 0x20 0xa0 0xe3
00010008: 0x03 0x30 0xa0 0xe3 0x03 0x40 0xa0 0xe3
00010010: 0x01 0x3a 0xa0 0xe3 0xff 0xef 0x01 0xe3
00010018: 0x00 0x30 0x4e 0xe3 0x2b 0x10 0xa0 0xe3
00010020: 0x0c 0xe0 0x83 0xe5 0x56 0x00 0xa0 0xe3

例:(qemu) x/10i 0x10000
0x00010000:  e32ff0d3      msr  CPSR_fsxc, #211 ; 0xd3
0x00010004:  e3a02003      mov  r2, #3  ; 0x3
0x00010008:  e3a03003      mov  r3, #3  ; 0x3
0x0001000c:  e3a04003      mov  r4, #3  ; 0x3
0x00010010:  e3a03a01      mov  r3, #4096       ; 0x1000
0x00010014:  e301efff      movw lr, #8191       ; 0x1fff
0x00010018:  e34e3000      movt r3, #57344      ; 0xe000
0x0001001c:  e3a0102b      mov  r1, #43 ; 0x2b
0x00010020:  e583e00c      str  lr, [r3, #12]
0x00010024:  e3a00056      mov  r0, #86 ; 0x56

まさか、ここで逆アセンブラができるとは‼便利ーーー‼‼

◎info roms

(qemu)info roms
addr=00000000 size=0x00001c mem=ram name="bootloader"
addr=00010000 size=0x0000d4 mem=ram name="/home/XXX/hello_serial/hello_serial.bin"

一つ前の記事のバイナリファイルは、メモリ上ではここに読み込まれていたのか‼‼
-kernelオプションをつけると、qemuがbootloaderを用意してくれるのかな。
さっそく覚えたアセンブラメモリダンプでbootloaderのあたりを見てみると、

(qemu) xp/8i 0
0x00000000:  e3a00000      mov  r0, #0  ; 0x0
0x00000004:  e3a01032      mov  r1, #50 ; 0x32
0x00000008:  e3811c0d      orr  r1, r1, #3328   ; 0xd00
0x0000000c:  e59f2000      ldr  r2, [pc, #0]    ; 0x14
0x00000010:  e59ff000      ldr  pc, [pc, #0]    ; 0x18
0x00000014:  00000100      andeq        r0, r0, r0, lsl #2
0x00000018:  00010000      andeq        r0, r1, r0
0x0000001c:  00000000      andeq        r0, r0, r0

◎info一覧
info romsに限らず、info mtree, info qtree, info trace-events, etcetc...重要な情報源になりそう。デバックにもかなり有効と思う。

(qemu) info
info balloon  -- show balloon information
info block  -- show the block devices
info blockstats  -- show block device statistics
info capture  -- show capture information
info chardev  -- show the character devices
info cpus  -- show infos for each CPU
info history  -- show the command line history
info jit  -- show dynamic compiler info
info kvm  -- show KVM information
info mice  -- show which guest mouse is receiving events
info migrate  -- show migration status
info mtree  -- show memory tree
info name  -- show the current VM name
info network  -- show the network state
info numa  -- show NUMA information
info pci  -- show PCI info
info pcmcia  -- show guest PCMCIA status
info profile  -- show profiling information
info qdm  -- show qdev device model list
info qtree  -- show device tree
info registers  -- show the cpu registers
info roms  -- show roms
info snapshots  -- show the currently saved VM snapshots
info status  -- show the current VM status (running|paused)
info trace-events  -- show available trace-events & their state
info usb  -- show guest USB devices
info usbhost  -- show host USB devices
info usernet  -- show user network stack connection states
info uuid  -- show the current VM UUID
info version  -- show the version of QEMU
info vnc  -- show the vnc server status

◎cpu n
番号nのCPUに切り替える