QEMU中TCG翻译流程
聲明:本文使用的qemu源碼版本為qemu-3.1.0-rc0
前言:qemu中采用事件驅動架構和并行架構相結合的方式來工作的。qemu中的線程主要有Vcpu線程,main_loop線程、I/O線程和workthread線程,其中main_loop屬于主線程。
1. 翻譯流程總體框架
2. 具體流程
? 1>?在vl.c的main函數中創建單板machine
? current_machine = MACHINE(object_new(object_class_get_name(OBJECT_CLASS(machine_class))));2>? 在啟動單板的過程中選擇了單板的參數如:
? ./qemu-system-ppc -M mac99此時會調用mac99單板對應的模型,其建模過程對應qemu源碼中的hw\ppc\mac_newworld.c文件
3> 初始化CPU(mac_newworld.c中調用)
? core99_machine_class_init->(mc->init=ppc_core99_init)? mac_newworld.c函數中ppc_core_init函數創建CPU具體過程如下:
? for (i = 0; i < smp_cpus; i++) {cpu = POWERPC_CPU(cpu_create(machine->cpu_type));env = &cpu->env;/* Set time-base frequency to 100 Mhz */cpu_ppc_tb_init(env, TBFREQ);qemu_register_reset(ppc_core99_reset, cpu);}?其中其中 cpu = POWERPC_CPU(cpu_create(machine->cpu_type))實現對于CPU的創建,其函數調用具體如下:(該函數原型位于cpu.c文件中)
? CPUState *cpu_create(const char *typename) {Error *err = NULL;CPUState *cpu = CPU(object_new(typename));object_property_set_bool(OBJECT(cpu), true, "realized", &err);if (err != NULL) {error_report_err(err);object_unref(OBJECT(cpu));exit(EXIT_FAILURE);}return cpu; }其中object_new函數會通過調用target/ppc/traslate_init.inc.c文件中的函數來創建CPU
4>?在創建CPU時每一個Vcpu都會創建一個vcpu線程(這就要和TCG翻譯有關了!!!)
? ppc_cpu_class_init->ppc_cpu_realize->qemu_init_vcpu5> 在cpus.c文件中qemu_init_vcpu函數的定義,該函數調用了qemu_tcg_init_vcpu函數
void qemu_init_vcpu(CPUState *cpu) {cpu->nr_cores = smp_cores;cpu->nr_threads = smp_threads;cpu->stopped = true;if (!cpu->as) {/* If the target cpu hasn't set up any address spaces itself,* give it the default one.*/cpu->num_ases = 1;cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);}if (kvm_enabled()) {qemu_kvm_start_vcpu(cpu);} else if (hax_enabled()) {qemu_hax_start_vcpu(cpu);} else if (hvf_enabled()) {qemu_hvf_start_vcpu(cpu);} else if (tcg_enabled()) {qemu_tcg_init_vcpu(cpu);} else if (whpx_enabled()) {qemu_whpx_start_vcpu(cpu);} else {qemu_dummy_start_vcpu(cpu);}while (!cpu->created) {qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);} }6> qemu_tcg_init_vcpu函數的定義
static void qemu_tcg_init_vcpu(CPUState *cpu) {char thread_name[VCPU_THREAD_NAME_SIZE];static QemuCond *single_tcg_halt_cond;static QemuThread *single_tcg_cpu_thread;static int tcg_region_inited;assert(tcg_enabled());/** Initialize TCG regions--once. Now is a good time, because:* (1) TCG's init context, prologue and target globals have been set up.* (2) qemu_tcg_mttcg_enabled() works now (TCG init code runs before the* -accel flag is processed, so the check doesn't work then).*/if (!tcg_region_inited) {tcg_region_inited = 1;tcg_region_init();}if (qemu_tcg_mttcg_enabled() || !single_tcg_cpu_thread) {cpu->thread = g_malloc0(sizeof(QemuThread));cpu->halt_cond = g_malloc0(sizeof(QemuCond));qemu_cond_init(cpu->halt_cond);if (qemu_tcg_mttcg_enabled()) {/* create a thread per vCPU with TCG (MTTCG) */parallel_cpus = true;snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/TCG",cpu->cpu_index);qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,cpu, QEMU_THREAD_JOINABLE);} else {/* share a single thread for all cpus with TCG */snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "ALL CPUs/TCG");qemu_thread_create(cpu->thread, thread_name,qemu_tcg_rr_cpu_thread_fn,cpu, QEMU_THREAD_JOINABLE);single_tcg_halt_cond = cpu->halt_cond;single_tcg_cpu_thread = cpu->thread;} #ifdef _WIN32cpu->hThread = qemu_thread_get_handle(cpu->thread); #endif} else {/* For non-MTTCG cases we share the thread */cpu->thread = single_tcg_cpu_thread;cpu->halt_cond = single_tcg_halt_cond;cpu->thread_id = first_cpu->thread_id;cpu->can_do_io = 1;cpu->created = true;} }該函數中調用(這里就創建了一個vcpu的線程,在執行客戶機代碼時進行翻譯)
qemu_thread_create(cpu->thread, thread_name, qemu_tcg_cpu_thread_fn,cpu, QEMU_THREAD_JOINABLE);?接下來其函數調用關系如下:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?7> 接下來重點解析cpu_exec()函數(該函數位于accel\tcg\cpu-exec.c文件下)
?在line724會調用 tb = tb_find(cpu, last_tb, tb_exit, cflags)用來查找TB(translation block)(這里的TB是已經翻譯為host? ?code?的代碼)
?查找到TB之后會調用cpu_loop_exec_tb(cpu, tb, &last_tb, &tb_exit)函數來執行翻譯好的主機代碼。
?8> tb_find函數解析
static inline TranslationBlock *tb_find(CPUState *cpu,TranslationBlock *last_tb,int tb_exit, uint32_t cf_mask) {TranslationBlock *tb;target_ulong cs_base, pc;uint32_t flags;tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);if (tb == NULL) {mmap_lock();tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);mmap_unlock();/* We add the TB in the virtual pc hash table for the fast lookup */atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);} #ifndef CONFIG_USER_ONLY/* We don't take care of direct jumps when address mapping changes in* system emulation. So it's not safe to make a direct jump to a TB* spanning two pages because the mapping for the second page can change.*/if (tb->page_addr[1] != -1) {last_tb = NULL;} #endif/* See if we can patch the calling TB. */if (last_tb) {tb_add_jump(last_tb, tb_exit, tb);}return tb; }該段代碼的主要功能是返回翻譯好的TB,首先調用tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask)函數查看該PC對應的代碼是否已經翻譯如果翻譯了,則直接返回TB如果沒有則需要調用tb_gen_code函數來進行翻譯。如果該基本塊不在cache中,則需要使用tb_gen_code函數進行翻譯并放到cache中。
其中 tb_lookup__cpu_state函數會調用cpu_get_tb_cpu_state獲取當前客戶機的pc寄存器中的值,接下來調用tb_jmp_cache_hash_func根據pc的值獲取存儲TB的hash表的索引,接下來調用atomic_rcu_read(&cpu->tb_jmp_cache[hash])函數來獲取cache中存儲的TB。tb_htable_lookup函數中通過調用get_page_addr_code函數獲取客戶機操作系統的物理內存地址(phys_pc)。
9>?tb_hen_code 目標機代碼翻譯為主機代碼的過程
??? phys_pc = get_page_addr_code(env, pc)? //獲取當前要翻譯的pc對應的物理地址
? ??tb = tb_alloc(pc)? ? //為該pc分配新的TB與之對應
? ? tb_gen_code->gen_intermediate_code //調用該函數將target code轉換為TCG操作碼
? ? tb_gen_code->?tcg_gen_code? ?//使用該函數將TCG操作碼轉換為host code
?
總結
以上是生活随笔為你收集整理的QEMU中TCG翻译流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pr_debug打印输出
- 下一篇: 你不知道的电脑36个小技巧(纪念2011