2012年10月12日
QEMUのsemihostingサポート
QEMU は ARM semihosting をサポートしているので、QEMU の標準入出力に対して、QEMU 上で動作するターゲットプログラム (ARM) から直接 printf() や fgets() ができます。
(ちなみに、弊社の PARTNER デバッガソフトウェアと exeGCC は、同様のメカニズムを VLINK として提供しています。これは ARM プロセッサに限定されない、MIPS や SH でも使用可能な、汎用のしくみです。)
(ちなみに、弊社の PARTNER デバッガソフトウェアと exeGCC は、同様のメカニズムを VLINK として提供しています。これは ARM プロセッサに限定されない、MIPS や SH でも使用可能な、汎用のしくみです。)
環境は、以前 Interface に記事を書いた時に作った VMWare の Ubuntu 11.10 64bit と、その時ビルドした QEMU 1.0 をそのまま使用しました。
以下のブログ記事を参考にしました。
QEMU ARM semihosting
ツールチェインは、Sourcery CodeBench Lite Edition の ARM processors > Download the EABI Release からダウンロードした arm-2012.03-56-arm-none-eabi-i686-pc-linux-gnu.tar.bz2 を ~/arm-eabi-2012.03 以下に展開して使用しました。
サンプルプログラム main.c は、リンク先の記事と同じものです。 リンカスクリプト generic-hosted.ld は、arm-none-eabi/lib/ 以下にありました。
これは、いったいどのようにして実現しているのでしょうか?
実は、割り込みをエミュレーションする関数の中で、直接 semihosting をエミュレートしています。
本来は svc 割り込みハンドラの中で行われる、機械語からの割り込み番号の取り出しなども、ここで行われています。もし割り込み番号が、ARM semihosting の 0x123456 や thumb の 0xab の場合は、割り込み状態に遷移せず、直接 semihosting をエミュレートするコードが実行され、結果が r0 に返るようになっています。
qemu-1.2.0/target-arm/helper.c
QEMU ARM semihosting
ツールチェインは、Sourcery CodeBench Lite Edition の ARM processors > Download the EABI Release からダウンロードした arm-2012.03-56-arm-none-eabi-i686-pc-linux-gnu.tar.bz2 を ~/arm-eabi-2012.03 以下に展開して使用しました。
サンプルプログラム main.c は、リンク先の記事と同じものです。 リンカスクリプト generic-hosted.ld は、arm-none-eabi/lib/ 以下にありました。
$ cp ~/arm-eabi-2012.03/arm-none-eabi/lib/generic-hosted.ld . $ vi generic-hosted.ld ... MEMORY { ram (rwx) : ORIGIN = 0x10000, LENGTH = 127M } ... $ ~/arm-eabi-2012.03/bin/arm-none-eabi-gcc -T generic-hosted.ld main.c -o main.elf $ ~/arm-eabi-2012.03/bin/arm-none-eabi-objcopy -O binary main.elf main.bin $ ~/qemu-1.0/bin/qemu-system-arm -semihosting -kernel main.bin Hello World! abcdefg ^D $ cat log.txt abcdefg確かに、ホストとの通信ができているようです。
これは、いったいどのようにして実現しているのでしょうか?
実は、割り込みをエミュレーションする関数の中で、直接 semihosting をエミュレートしています。
本来は svc 割り込みハンドラの中で行われる、機械語からの割り込み番号の取り出しなども、ここで行われています。もし割り込み番号が、ARM semihosting の 0x123456 や thumb の 0xab の場合は、割り込み状態に遷移せず、直接 semihosting をエミュレートするコードが実行され、結果が r0 に返るようになっています。
qemu-1.2.0/target-arm/helper.c
/* Handle a CPU exception. */ void do_interrupt(CPUARMState *env) { ... case EXCP_SWI: if (semihosting_enabled) { /* Check for semihosting interrupt. */ if (env->thumb) { mask = arm_lduw_code(env->regs[15] - 2, env->bswap_code) & 0xff; } else { mask = arm_ldl_code(env->regs[15] - 4, env->bswap_code) & 0xffffff; } /* Only intercept calls from privileged modes, to provide some semblance of security. */ if (((mask == 0x123456 && !env->thumb) || (mask == 0xab && env->thumb)) && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { env->regs[0] = do_arm_semihosting(env); return; } } ... static void do_interrupt_v7m(CPUARMState *env) { ... case EXCP_BKPT: if (semihosting_enabled) { int nr; nr = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff; if (nr == 0xab) { env->regs[15] += 2; env->regs[0] = do_arm_semihosting(env); return; } } ...この do_arm_semihosting() は、target-arm/arm-semi.c の中で実装されていて、r0 の値に応じて、様々なシステムコールをエミュレーションする、巨大な switch 文になっています。