QEMUの続き。独自ターゲットでビルドしてみる。myriscvx64
コンフィグレーションを用意した。
MYRISCVXCPU
はCPUMYRISCVXState
を包むラッパーのようなものらしい。env
メンバ変数としてMYRISCVXState
を含んでおり、cfg
メンバはRISC-Vのオプションを指定できるようになっている。
qemu/target/myriscvx/cpu.h
/** * MYRISCVXCPU: * @env: #CPUMYRISCVXState * * A MYRISCVX CPU. */ typedef struct MYRISCVXCPU { /*< private >*/ CPUState parent_obj; /*< public >*/ CPUNegativeOffsetState neg; CPUMYRISCVXState env; /* Configuration Settings */ struct { bool ext_i; bool ext_e; bool ext_g; bool ext_m; bool ext_a; bool ext_f; bool ext_d; bool ext_c; bool ext_s; bool ext_u; bool ext_h; bool ext_counters; bool ext_ifencei; bool ext_icsr; char *priv_spec; char *user_spec; bool mmu; bool pmp; } cfg; } MYRISCVXCPU;
次はCPU_RESOLVING_TYPE
というマクロを定義しなければならないらしい。ここでは"myriscvx-cpu"
という文字列を定義している。
target/myriscvx/cpu.h
#define TYPE_MYRISCVX_CPU "myriscvx-cpu" #define MYRISCVX_CPU_TYPE_SUFFIX "-" TYPE_MYRISCVX_CPU #define MYRISCVX_CPU_TYPE_NAME(name) (name MYRISCVX_CPU_TYPE_SUFFIX) #define CPU_RESOLVING_TYPE TYPE_MYRISCVX_CPU #define TYPE_MYRISCVX_CPU_ANY MYRISCVX_CPU_TYPE_NAME("any") #define TYPE_MYRISCVX_CPU_BASE64 MYRISCVX_CPU_TYPE_NAME("myriscvx64")
次はMYRISCVXのhelper.h
を定義しなければならない。これはいったい何だろう?
riscv/helper.h
では以下のようなマクロが定義されている。
qemu/target/riscv/helper.h
/* Exceptions */ DEF_HELPER_2(raise_exception, noreturn, env, i32) /* Floating Point - rounding mode */ DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_WG, void, env, i32) /* Floating Point - fused */ DEF_HELPER_FLAGS_4(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(fmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(fmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
これはいったいどういう定義になっているんだろう?
qemu/include/exec/helper-head.h
#define DEF_HELPER_2(name, ret, t1, t2) \ DEF_HELPER_FLAGS_2(name, 0, ret, t1, t2)
qemu/include/exec/helper-gen.h
#define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2)) \ { \ TCGTemp *args[2] = { dh_arg(t1, 1), dh_arg(t2, 2) }; \ tcg_gen_callN(HELPER(name), dh_retvar(ret), 2, args); \ } #define DEF_HELPER_FLAGS_4(name, flags, ret, t1, t2, t3, t4) \ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) \ dh_arg_decl(t1, 1), dh_arg_decl(t2, 2), \ dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ { \ TCGTemp *args[4] = { dh_arg(t1, 1), dh_arg(t2, 2), \ dh_arg(t3, 3), dh_arg(t4, 4) }; \ tcg_gen_callN(HELPER(name), dh_retvar(ret), 4, args); \ }
例えばException
定義であれば、以下のように変換されている。これはヘルプ関数を定義しているのだろうか?
DEF_HELPER_2(raise_exception, noreturn, env, i32) // --> DEF_HELPER_FLAGS_2(raise_exception, 0, noreturn, env, i32) // --> #define DEF_HELPER_FLAGS_2(name, flags, ret, t1, t2) \ static inline void glue(gen_helper_, raise_exception)(dh_retvar_decl(noreturn) \ dh_arg_decl(env, 1), dh_arg_decl(i32, 2)) \ { \ TCGTemp *args[2] = { dh_arg(env, 1), dh_arg(i32, 2) }; \ tcg_gen_callN(HELPER(raise_exception), dh_retvar(noreturn), 2, args); \ }
とりあえず空白のhelper.h
を作成して次に進む。次はQEMU_ARCH
の定義が必要だ。これはarch_init.c
に定義されている。
diff --git a/arch_init.c b/arch_init.c index d9eb0ec1dd..f4690ff1f0 100644 --- a/arch_init.c +++ b/arch_init.c @@ -91,6 +91,8 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_XTENSA) #define QEMU_ARCH QEMU_ARCH_XTENSA +#elif defined(TARGET_MYRISCVX) +#define QEMU_ARCH QEMU_ARCH_MYRISCVX #endif const uint32_t arch_type = QEMU_ARCH; diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 71a7a285ee..316530e68a 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -25,6 +25,7 @@ enum { QEMU_ARCH_HPPA = (1 << 18), QEMU_ARCH_RISCV = (1 << 19), QEMU_ARCH_RX = (1 << 20), + QEMU_ARCH_MYRISCVX = (1 << 21), QEMU_ARCH_NONE = (1 << 31), };
さらにコンパイルを進めると今度はtranslate.c
の定義が必要な関数群が無いと言われている。
/usr/bin/ld: accel/tcg/translate-all.o: in function `cpu_restore_state_from_tb': /home/msyksphinz/work/riscv/qemu/accel/tcg/translate-all.c:372: undefined reference to `restore_state_to_opc' /usr/bin/ld: accel/tcg/translate-all.o: in function `tb_gen_code': /home/msyksphinz/work/riscv/qemu/accel/tcg/translate-all.c:1718: undefined reference to `gen_intermediate_code' /usr/bin/ld: accel/tcg/translate-all.o: in function `tb_check_watchpoint': /home/msyksphinz/work/riscv/qemu/accel/tcg/translate-all.c:2144: undefined reference to `cpu_get_tb_cpu_state' /usr/bin/ld: accel/tcg/cputlb.o: in function `get_page_addr_code_hostp': /home/msyksphinz/work/riscv/qemu/accel/tcg/cputlb.c:1165: undefined reference to `cpu_mmu_index' /usr/bin/ld: accel/tcg/cputlb.o: in function `cpu_ldub_data_ra':
void restore_state_to_opc(CPURXState *env, TranslationBlock *tb, target_ulong *data) { env->pc = data[0]; }
次はcpu_get_tb_cpu_state()
これはおそらく例外発生時のCPUの情報を退避するためのものだと思う。
qemu/accel/tcg/translate-all.c
} else { /* The exception probably happened in a helper. The CPU state should have been saved before calling it. Fetch the PC from there. */ CPUArchState *env = cpu->env_ptr; target_ulong pc, cs_base; tb_page_addr_t addr; uint32_t flags; cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); ...
qemu/target/riscv/cpu.h
static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; #ifdef CONFIG_USER_ONLY *flags = TB_FLAGS_MSTATUS_FS; #else *flags = cpu_mmu_index(env, 0); if (riscv_cpu_fp_enabled(env)) { *flags |= env->mstatus & MSTATUS_FS; } #endif }
これをMYRISCVXに追加する。
static inline void cpu_get_tb_cpu_state(CPUMYRISCVXState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) { *pc = env->pc; *cs_base = 0; #ifdef CONFIG_USER_ONLY *flags = TB_FLAGS_MSTATUS_FS; #else *flags = cpu_mmu_index(env, 0); if (riscv_cpu_fp_enabled(env)) { *flags |= env->mstatus & MSTATUS_FS; } #endif } int myriscvx_cpu_mmu_index(CPUMYRISCVXState *env, bool ifetch) { #ifdef CONFIG_USER_ONLY return 0; #else return env->priv; #endif }