2012年10月12日

QEMUのsemihostingサポート

QEMU は ARM semihosting をサポートしているので、QEMU の標準入出力に対して、QEMU 上で動作するターゲットプログラム (ARM) から直接 printf() や fgets() ができます。

(ちなみに、弊社の 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/ 以下にありました。
$ 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 文になっています。

トラックバックURL

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード