2009年2月7日土曜日

QEMU stdvga

http://hrb.osask.jp/wiki/?advance/QEMUVGA

vl.c の中でオプション解析。デフォルトは cirrus_vga_enable になってるみたい。

pci/isa_vga_init() で作ったビデオカードが stdvga 相当になるみたい。
(他はそれぞれ、vga の代わりに cirrus  や vmsvga が付いた初期化関数を使う)

2009年2月5日木曜日

QEMU base emulator

Bishop エミュレータ

やはりこの会社は、Android もやってるみたい。

2009年1月30日金曜日

parent_irq

QEMU の仮想デバイス (例えば i8259) には、parent_irq というメンバーが存在することが多い。

これにより、複数のデバイスの IRQ を繋げることができるようだ。なるほど、よくできてる。

大抵は、初期化の時に cpuirq などの、CPU と直結する IRQ (ハンドラの中で cpu_interrupt() を呼ぶ) を作っておいて、これをトップレベルの parent_irq とする。

2009年1月24日土曜日

ldl_code の続き

grep しても見つからないわけだ。disas_arm_insn 内の ldl_code の正体は、例のこれ。
#define ACCESS_TYPE (NB_MMU_MODES + 1)
#define MEMSUFFIX _code
#define env cpu_single_env

#define DATA_SIZE 1
#include "softmmu_header.h"

#define DATA_SIZE 2
#include "softmmu_header.h"

#define DATA_SIZE 4
#include "softmmu_header.h"

#define DATA_SIZE 8
#include "softmmu_header.h"

#undef ACCESS_TYPE
#undef MEMSUFFIX
#undef env

#endif
これで、softmmu_header.h 内の
/* generic load/store macros */

static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
{
    int index;
    RES_TYPE res;
    target_ulong addr;
    unsigned long physaddr;
    int mmu_idx;

    addr = ptr;
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
    mmu_idx = CPU_MMU_INDEX;
    if (__builtin_expect(env->tlb_table[mmu_idx][index].ADDR_READ !=
                         (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) {
        res = glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, mmu_idx);
    } else {
        physaddr = addr + env->tlb_table[mmu_idx][index].addend;
        res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr);
    }
    return res;
}
これが tatic inline uint32_t  ldl_code(target_ulong ptr) に特殊化される。
/* generic load/store macros */

static inline uint32_t ldl_code(target_ulong ptr)
{
    int index;
    uint32_t res;
    target_ulong addr;
    unsigned long physaddr;
    int mmu_idx;

    addr = ptr;
    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
    mmu_idx = CPU_MMU_INDEX;
    if (__builtin_expect(env->tlb_table[mmu_idx][index].ADDR_READ !=
                         (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))), 0)) {
        res = __ldl_cmmu(addr, mmu_idx);
    } else {
        physaddr = addr + env->tlb_table[mmu_idx][index].addend;
        res = ldl_raw((uint8_t *)physaddr);
    }
    return res;
}
考えてみれば当たり前。全ての (QEMU 上の仮想 RAM への) メモリアクセスは (QEMU の仮想) MMU 経由で行わないといけないのだから。

2009年1月23日金曜日

disas_arm_insn

target-arm/translate.c の
static void disas_arm_insn(CPUState * env, DisasContext *s)
{
    unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh;

    insn = ldl_code(s->pc);
ここで s->pc は、ロードされたターゲットのマシン語の pc だ。

cpu-all.h を見ると。
#define ldl_code(p) ldl_raw(p)
#define ldl_raw(p) ldl_p(laddr((p)))
#define laddr(x) (uint8_t *)(long)(x)
#define ldl_p(p) ldl_le_p(p)  /* !defined(TARGET_WORDS_BIGENDIAN)  */
static inline int ldl_le_p(void *ptr)
{
    return *(uint32_t *)ptr;
}
う~ん、なぜこれで命令が取ってこれるのかよくわからない。
メモリのどこに、ターゲットの命令はロードされてるんだ ?
たぶん phys_ram_base とかだと思っていたんだけど、違うのかな ?

追記

これは CONFIG_USER_ONLY 版のコードなので、全く関係無かった。

しかし、どこに定義があるんだ… grep した限りでは見つけられなかった。

2009年1月22日木曜日

cpu_index

exec.c の cpu_exec_init() を見ればわかるけど、全ての cpu_init()  (実体は cpu_arm_init() などの define) で作った CPU (実体は target/ 以下の struct CPUARMState など) は、全てグローバル変数 first_cpu から作った順番に辿ることができる。

最初の CPU が cpu_index == 0 で、順番に全て番号が付いているので、番号で識別できる。

cpu_single_env も似たような感じに使われているけど、これは cpu-exec.c の cpu_exec 関数の内部だけで有効。

686    /* fail safe : never use cpu_single_env outside cpu_exec() */
687    cpu_single_env = NULL;
688    return ret;
689}
ここらへんからも、QEMU はマルチスレッド対応が考えられていないことがわかる。
/* 'pc' is the host PC at which the exception was raised. 'address' is
   the effective address of the memory exception. 'is_write' is 1 if a
   write caused the exception and otherwise 0'. 'old_set' is the
   signal set which should be restored */
static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
                                    int is_write, sigset_t *old_set,
                                    void *puc)
{
    TranslationBlock *tb;
    int ret;

    if (cpu_single_env)
        env = cpu_single_env; /* XXX: find a correct solution for multithread */

2009年1月19日月曜日

cpu_interrpt() is probably not threadsafe.

う~む。
/* mask must never be zero, except for A20 change call */
void cpu_interrupt(CPUState *env, int mask)
{
。。。
    /* FIXME: This is probably not threadsafe.  A different thread could
       be in the middle of a read-modify-write operation.  */
    env->interrupt_request |= mask;
#if defined(USE_NPTL)
    /* FIXME: TB unchaining isn't SMP safe.  For now just ignore the
       problem and hope the cpu will stop of its own accord.  For userspace
       emulation this often isn't actually as bad as it sounds.  Often
       signals are used primarily to interrupt blocking syscalls.  */
#else
       。。。
}