QEMU RISCV TCG 详解二 -- RISCV CPU Representation
在 QEMU 里,如何表征 RISCV CPU 呢?该代码就位于 qemu/target/riscv/cpu-qom.h 中。如下:
// qemu/target/riscv/cpu-qom.h:60
OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU)
经过宏扩展(Macro Preprocess)后,有
# 60 "../target/riscv/cpu-qom.h"
typedef struct ArchCPU RISCVCPU;
typedef struct ArchCPU ArchCPU;
typedef struct RISCVCPUClass RISCVCPUClass;
typedef ArchCPU *ArchCPU_autoptr;
typedef GList *ArchCPU_listautoptr;
typedef GSList *ArchCPU_slistautoptr;
typedef GQueue *ArchCPU_queueautoptr;
其中类型 ArchCPU 就是 QEMU中,对某一 ISA 的 CPU 的表征(Representation)。那么,对于 RISCV ISA来说,自然就是 类型 RISCVCPU 了。
另外,基于 QEMU 的 QOM 框架,即基于 C 语言来实现的类似与C++中的面向对象机制(Object Oriented Mechanism),有,类型 RISCVCPU 为 InstanceType,其对应的 ClassType 为 RISCVCPUClass。就像之前,C++还没有自己专用的编译器时,需要先将 C++ 编译成 C 再 编译成二进制码那样。也类似于 Objective-C 中的运行时(runtime)那样。
即,如果由 C++ 实现为:
// pesudo code of RISCV CPU in C++ as in C with QEMU QOM
class RISCVCPUClass {// data membersRISCVCPU cpu;// with some methodsauto methods();// class data membersstatic class_members;// with some class methodsauto class_methods();
}
即,类型 RISCVCPU 封装了 RISCVCPUClass 的成员及成员函数,RISCVCPUClass 封装了 类成员及函数。其中,实例类型(InstanceType)中有一成员 ObjectClass *class 指向其 RISCVCPUClass。注意,成员函数(member methods)是存放在 Class 中的,而非实例中的。实例只包含对应的数据成员(data member)。
对于 RISCVCPU 的 QOM 构建过程,会在后续博文进行详细讲解。这就可以简单地,类似上面 C++ 实现的方式进行理解。
那么,对于 RISCV 的 ArchCPU 的定义如下:
/** RISCVCPU:* @env: #CPURISCVState** A RISCV CPU.*/
struct ArchCPU {CPUState parent_obj;CPURISCVState env;GDBFeature dyn_csr_feature;GDBFeature dyn_vreg_feature;/* Configuration Settings */RISCVCPUConfig cfg;RISCVSATPModes satp_modes;QEMUTimer *pmu_timer;/* A bitmask of Available programmable counters */uint32_t pmu_avail_ctrs;/* Mapping of events to counters */GHashTable *pmu_event_ctr_map;const GPtrArray *decoders;
};
其中包含了 CPUState parent_obj 以及 CPURISCVState env。
1. CPUState parent_obj 包含所有不同架构的CPU的共同信息。类似如下C++实现:
// pesudo code of RISCV CPU in C++ as in C with QEMU QOM
class RISCVCPU: public CPUState {// data membersauto data_members;
}class RISCVCPUClass: public CPUClass {// data membersRISCVCPU cpu;// with some methodsauto methods();// class data membersstatic class_members;// with some class methodsauto class_methods();
}
2. CPURISCVState env 包含特定架构特有的状态信息,如各个通用寄存器、状态寄存器等。其中 RISCV ISA 的如下:
typedef struct CPUArchState CPURISCVState;
struct CPUArchState {target_ulong gpr[32];target_ulong gprh[32]; /* 64 top bits of the 128-bit registers *//* vector coprocessor state. */...target_ulong pc;.../* Floating-Point state */.../* RISCVMXL, but uint32_t for vmstate migration */.../* 128-bit helpers upper part return value */....../* RNMI */...
};
其中,在 CPUState 结构体中的最后一个成员,CPUNegativeOffsetState neg,是以ArchCPU::env 为基准来访问,即:
/*** struct CPUState - common state of one CPU core or thread.* @neg: The architectural register state ("cpu_env") immediately follows* CPUState in ArchCPU and is passed to TCG code. The @neg structure holds* some common TCG CPU variables which are accessed with a negative offset* from cpu_env.*/
struct CPUState {
.../** MUST BE LAST in order to minimize the displacement to CPUArchState.*/char neg_align[-sizeof(CPUNegativeOffsetState) % 16] QEMU_ALIGNED(16);CPUNegativeOffsetState neg;
};
/** RISCVCPU:* @env: #CPURISCVState** A RISCV CPU.*/
struct ArchCPU {CPUState parent_obj;CPURISCVState env;
...
}
由此,可见 CPUState parent_obj 在地址中是位于 CPURISCVState env 前,其最后成员 CPUNegativeOffsetState neg 就刚好位于 env 前面。这个设计,有助于二进制转译后的加速优化,直接通过 env 指针的加减就可以访问对应的成员。后面遇到这使用情况再详细说明。
另外,该设计也有如下使用方式:
static inline CPUArchState *cpu_env(CPUState *cpu)
{/* We validate that CPUArchState follows CPUState in cpu-target.c */return (CPUArchState *)(cpu + 1);
}/*** env_archcpu(env)* @env: The architecture environment** Return the ArchCPU associated with the environment.*/
static inline ArchCPU *env_archcpu(CPUArchState *env)
{return (void *)env - sizeof(CPUState);
}
另外,还有就是对应的 RISCVCPUClass,如下:
/*** RISCVCPUClass:* @parent_realize: The parent class' realize handler.* @parent_phases: The parent class' reset phase handlers.** A RISCV CPU model.*/
struct RISCVCPUClass {CPUClass parent_class;DeviceRealize parent_realize;ResettablePhases parent_phases;RISCVCPUDef *def;
};
/*** CPUClass:
...* Represents a CPU family or model.*/
struct CPUClass {
...
/* when TCG is not available, this pointer is NULL */const TCGCPUOps *tcg_ops;
...
};
其中 tcg_ops 就是 TCG 对特定架构CPU的操作函数,其主要成员如下:
const TCGCPUOps riscv_tcg_ops = {.mttcg_supported = true,.guest_default_memory_order = 0,.initialize = riscv_translate_init,.translate_code = riscv_translate_code,.get_tb_cpu_state = riscv_get_tb_cpu_state,.synchronize_from_tb = riscv_cpu_synchronize_from_tb,.restore_state_to_opc = riscv_restore_state_to_opc,.mmu_index = riscv_cpu_mmu_index,#ifndef CONFIG_USER_ONLY.tlb_fill = riscv_cpu_tlb_fill,.pointer_wrap = riscv_pointer_wrap,.cpu_exec_interrupt = riscv_cpu_exec_interrupt,.cpu_exec_halt = riscv_cpu_has_work,.cpu_exec_reset = cpu_reset,.do_interrupt = riscv_cpu_do_interrupt,.do_transaction_failed = riscv_cpu_do_transaction_failed,.do_unaligned_access = riscv_cpu_do_unaligned_access,.debug_excp_handler = riscv_cpu_debug_excp_handler,.debug_check_breakpoint = riscv_cpu_debug_check_breakpoint,.debug_check_watchpoint = riscv_cpu_debug_check_watchpoint,
#endif /* !CONFIG_USER_ONLY */
};
在 CPUClass 初始化时定义,如下:
/*{.name = TYPE_RISCV_CPU,.parent = TYPE_CPU,.instance_size = sizeof(RISCVCPU),.instance_align = __alignof(RISCVCPU),// 实例初始化函数,类似 instance constructor..instance_init = riscv_cpu_init,.abstract = true,.class_size = sizeof(RISCVCPUClass),// 类初始化函数,类似 class constructor..class_init = riscv_cpu_common_class_init,.class_base_init = riscv_cpu_class_base_init,
}*/static void riscv_cpu_common_class_init(ObjectClass *c, const void *data)
{RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);CPUClass *cc = CPU_CLASS(c);DeviceClass *dc = DEVICE_CLASS(c);ResettableClass *rc = RESETTABLE_CLASS(c);device_class_set_parent_realize(dc, riscv_cpu_realize,&mcc->parent_realize);resettable_class_set_parent_phases(rc, NULL, riscv_cpu_reset_hold, NULL,&mcc->parent_phases);cc->class_by_name = riscv_cpu_class_by_name;cc->dump_state = riscv_cpu_dump_state;cc->set_pc = riscv_cpu_set_pc;cc->get_pc = riscv_cpu_get_pc;cc->gdb_read_register = riscv_cpu_gdb_read_register;cc->gdb_write_register = riscv_cpu_gdb_write_register;cc->gdb_stop_before_watchpoint = true;cc->disas_set_info = riscv_cpu_disas_set_info;
#ifndef CONFIG_USER_ONLYcc->sysemu_ops = &riscv_sysemu_ops;cc->get_arch_id = riscv_get_arch_id;
#endifcc->gdb_arch_name = riscv_gdb_arch_name;
#ifdef CONFIG_TCGcc->tcg_ops = &riscv_tcg_ops;
#endif /* CONFIG_TCG */device_class_set_props(dc, riscv_cpu_properties);
}
至此,对于 RISCV CPU 在 QEMU 是如何表征有了大致上的了解。后续文章,就会可以继续推进 二进制转译的研究了。敬请期待。