QEMUの続き。QEMUのRISC-V対応においてバイナリのシミュレーション方法とログの出し方が分かったわけだが、これをMYRISCVXに移植したい。つまり同じコマンドをqemu-system-myriscvx64
に適用することを考える。
まずは変換のための初期化関数から。
qemu/target/myriscvx/cpu.c
static void myriscvx_cpu_class_init(ObjectClass *c, void *data) ... #ifdef CONFIG_TCG cc->tcg_initialize = myriscvx_translate_init; // cc->tlb_fill = myriscvx_cpu_tlb_fill; #endif
qemu/target/myriscvx/translate.c
void myriscvx_translate_init(void) { int i; /* cpu_gpr[0] is a placeholder for the zero register. Do not use it. */ /* Use the gen_set_gpr and gen_get_gpr helper functions when accessing */ /* registers, unless you specifically block reads/writes to reg 0 */ cpu_gpr[0] = NULL; for (i = 1; i < 32; i++) { cpu_gpr[i] = tcg_global_mem_new(cpu_env, offsetof(CPUMYRISCVXState, gpr[i]), myriscvx_int_regnames[i]); } cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPUMYRISCVXState, pc), "pc"); load_res = tcg_global_mem_new(cpu_env, offsetof(CPUMYRISCVXState, load_res), "load_res"); load_val = tcg_global_mem_new(cpu_env, offsetof(CPUMYRISCVXState, load_val), "load_val"); }
cpu_gpr
とcpu_pc
などの変数はグローバル変数として定義してある。これは何のために必要なんだろう?
qemu/target/myriscvx/translate.c
/* global register indices */ static TCGv cpu_gpr[32], cpu_pc; // static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ static TCGv load_res; static TCGv load_val;
そもそもTCGv
という型名の意味は、Tiny Code Generator Variableのことらしい。
qemu/include/tcg/tcg.h
typedef struct TCGv_i32_d *TCGv_i32; typedef struct TCGv_i64_d *TCGv_i64; typedef struct TCGv_ptr_d *TCGv_ptr; typedef struct TCGv_vec_d *TCGv_vec;
とりあえず、これらの変数を確保するらしい。この状態でビルドして動作を確認する。
qemu-system-myriscvx64 --machine virt --d in_asm --nographic --kernel rv64ui-p-simple QEMU 5.0.0 monitor - type 'help' for more information (qemu) zsh: segmentation fault (core dumped) --machine virt --d in_asm --nographic --kernel
うーん、落ちた。分からないのでGDBでチェックする。
#0 0x0000000000000000 in () #1 0x000000000048b7a5 in tlb_fill (cpu=0xc78fc0, addr=0, size=0, access_type=MMU_INST_FETCH, mmu_idx=0, retaddr=0) at /home/msyksphinz/work/riscv/qemu/accel/tcg/cputlb.c:1017 #2 0x000000000048bdfb in get_page_addr_code_hostp (env=0xc819e0, addr=0, hostp=0x0) at /home/msyksphinz/work/riscv/qemu/accel/tcg/cputlb.c:1172 #3 0x000000000048bf22 in get_page_addr_code (env=0xc819e0, addr=0) at /home/msyksphinz/work/riscv/qemu/accel/tcg/cputlb.c:1204 #4 0x00000000004a4195 in tb_htable_lookup (cpu=0xc78fc0, pc=0, cs_base=0, flags=0, cf_mask=4278190080) at /home/msyksphinz/work/riscv/qemu/accel/tcg/cpu-exec.c:337 #5 0x00000000004a36da in tb_lookup__cpu_state (cpu=0xc78fc0, pc=0x7fffb3fef538, cs_base=0x7fffb3fef530, flags=0x7fffb3fef52c, cf_mask=4278190080) at /home/msyksphinz/work/riscv/qemu/include/exec/tb-lookup.h:43 #6 0x00000000004a444a in tb_find (cpu=0xc78fc0, last_tb=0x0, tb_exit=0, cf_mask=0) at /home/msyksphinz/work/riscv/qemu/accel/tcg/cpu-exec.c:404 #7 0x00000000004a4d1d in cpu_exec (cpu=0xc78fc0) at /home/msyksphinz/work/riscv/qemu/accel/tcg/cpu-exec.c:731 #8 0x0000000000468061 in tcg_cpu_exec (cpu=0xc78fc0) at /home/msyksphinz/work/riscv/qemu/cpus.c:1405 #9 0x000000000046835a in qemu_tcg_rr_cpu_thread_fn (arg=0xc78fc0) at /home/msyksphinz/work/riscv/qemu/cpus.c:1507 #10 0x0000000000707e5e in qemu_thread_start (args=0xc8e350) at /home/msyksphinz/work/riscv/qemu/util/qemu-thread-posix.c:519 #11 0x00007ffffe367164 in start_thread (arg=<optimized out>) at pthread_create.c:486 #12 0x00007ffffe28adef in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
うーん、これはtb_lookup__cpu_state
のところが怪しい気がする。ん?というか基本的にtlb_fill()
が実装されていないということか?
static void myriscvx_cpu_class_init(ObjectClass *c, void *data) { ... // cc->tlb_fill = myriscvx_cpu_tlb_fill; }
コメントアウトしてた。cpu_helper.c
に実装するらしい。いろいろ省略して、とりあえず物理アドレスしか使わないような実装にしてみる。
qemu/target/myriscvx/cpu_helper.c
bool myriscvx_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { MYRISCVXCPU *cpu = MYRISCVX_CPU(cs); CPUMYRISCVXState *env = &cpu->env; ... if (/* myriscvx_cpu_virt_enabled(env) || */m_mode_two_stage || hs_mode_two_stage) { /* Two stage lookup */ ret = get_physical_address(env, &pa, &prot, address, access_type, mmu_idx, true, true); ...
static int get_physical_address(CPUMYRISCVXState *env, hwaddr *physical, int *prot, target_ulong addr, int access_type, int mmu_idx, bool first_stage, bool two_stage) { *physical = addr; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TRANSLATE_SUCCESS; }
これでビルドしてみたが、やはり途中で落ちる。まだ別の機能を実装していないからだな。調査を続ける。