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 文になっています。